Add support for reflected math operations ✖️ (#22478)

# Objective

I want the ability to do math operations within a scripting environment
powered by reflection, where the types are not known at compile time.

## Solution

- Define new function pointer holder structs: `ReflectAdd`,
`ReflectSub`, `ReflectMul`, `ReflectDiv`, `ReflectRem`,
`ReflectAddAssign`, `ReflectSubAssign`, `ReflectMulAssign`,
`ReflectDivAssign`, `ReflectRemAssign`.
- Implement them as appropriate for `std` types, including all numerical
primitives and `Duration`.

## Testing

Added tests to exhaustively check math operations on all numerical
primitives, and `Duration`.
This commit is contained in:
Christian Hughes
2026-01-12 14:49:54 -06:00
committed by GitHub
parent d93f15f37e
commit f8ea309657
3 changed files with 934 additions and 22 deletions
+436 -17
View File
@@ -24,7 +24,7 @@ impl_reflect_opaque!(bool(
PartialEq,
Serialize,
Deserialize,
Default
Default,
));
impl_reflect_opaque!(char(
Clone,
@@ -33,7 +33,7 @@ impl_reflect_opaque!(char(
PartialEq,
Serialize,
Deserialize,
Default
Default,
));
impl_reflect_opaque!(u8(
Clone,
@@ -42,7 +42,17 @@ impl_reflect_opaque!(u8(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(u16(
Clone,
@@ -51,7 +61,17 @@ impl_reflect_opaque!(u16(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(u32(
Clone,
@@ -60,7 +80,17 @@ impl_reflect_opaque!(u32(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(u64(
Clone,
@@ -69,7 +99,17 @@ impl_reflect_opaque!(u64(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(u128(
Clone,
@@ -78,7 +118,17 @@ impl_reflect_opaque!(u128(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(usize(
Clone,
@@ -87,7 +137,17 @@ impl_reflect_opaque!(usize(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(i8(
Clone,
@@ -96,7 +156,17 @@ impl_reflect_opaque!(i8(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(i16(
Clone,
@@ -105,7 +175,17 @@ impl_reflect_opaque!(i16(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(i32(
Clone,
@@ -114,7 +194,17 @@ impl_reflect_opaque!(i32(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(i64(
Clone,
@@ -123,7 +213,17 @@ impl_reflect_opaque!(i64(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(i128(
Clone,
@@ -132,7 +232,17 @@ impl_reflect_opaque!(i128(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(isize(
Clone,
@@ -141,7 +251,17 @@ impl_reflect_opaque!(isize(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(f32(
Clone,
@@ -149,7 +269,17 @@ impl_reflect_opaque!(f32(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_reflect_opaque!(f64(
Clone,
@@ -157,7 +287,17 @@ impl_reflect_opaque!(f64(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
Mul,
MulAssign,
Div,
DivAssign,
Rem,
RemAssign,
));
impl_type_path!(str);
@@ -523,8 +663,22 @@ impl<T: TypePath + ?Sized> TypePath for &'static mut T {
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use bevy_reflect::{FromReflect, PartialReflect};
use core::f32::consts::{PI, TAU};
use core::{
any::TypeId,
f32::consts::{PI, TAU},
ops::{Add, Div, Mul, Rem, Sub},
};
use crate::{
prelude::{
ReflectAdd, ReflectDivAssign, ReflectMulAssign, ReflectRem, ReflectRemAssign,
ReflectSubAssign,
},
std_traits::{ReflectAddAssign, ReflectDiv, ReflectMul, ReflectSub},
Reflect, TypeRegistry,
};
#[test]
fn should_partial_eq_char() {
@@ -559,4 +713,269 @@ mod tests {
let output = <&'static str as FromReflect>::from_reflect(&expected).unwrap();
assert_eq!(expected, output);
}
#[test]
fn should_add() {
fn check_add<T: Reflect + Add<Output = T> + Copy + 'static>(
registry: &TypeRegistry,
mut a: T,
b: T,
result: T,
) {
let reflect_add = registry
.get_type_data::<ReflectAdd>(TypeId::of::<T>())
.unwrap();
let reflect_add_assign = registry
.get_type_data::<ReflectAddAssign>(TypeId::of::<T>())
.unwrap();
assert_eq!(
reflect_add
.add(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&result),
Some(true)
);
reflect_add_assign.add_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a.reflect_partial_eq(&result), Some(true));
assert!(reflect_add.add(Box::new(a), Box::new("not a T")).is_err());
assert!(reflect_add.add(Box::new("not a T"), Box::new(b)).is_err());
assert!(reflect_add_assign
.add_assign(&mut a, Box::new("not a T"))
.is_err());
assert!(reflect_add_assign
.add_assign(&mut "not a T", Box::new(b))
.is_err());
}
let registry = TypeRegistry::new();
check_add::<u8>(&registry, 10, 5, 15);
check_add::<u16>(&registry, 10, 5, 15);
check_add::<u32>(&registry, 10, 5, 15);
check_add::<u64>(&registry, 10, 5, 15);
check_add::<u128>(&registry, 10, 5, 15);
check_add::<usize>(&registry, 10, 5, 15);
check_add::<i8>(&registry, 10, 5, 15);
check_add::<i16>(&registry, 10, 5, 15);
check_add::<i32>(&registry, 10, 5, 15);
check_add::<i64>(&registry, 10, 5, 15);
check_add::<i128>(&registry, 10, 5, 15);
check_add::<isize>(&registry, 10, 5, 15);
check_add::<f32>(&registry, 1.5, 2.5, 4.0);
check_add::<f64>(&registry, 1.5, 2.5, 4.0);
}
#[test]
fn should_sub() {
fn check_sub<T: Reflect + Sub<Output = T> + Copy + 'static>(
registry: &TypeRegistry,
mut a: T,
b: T,
result: T,
) {
let reflect_sub = registry
.get_type_data::<ReflectSub>(TypeId::of::<T>())
.unwrap();
let reflect_sub_assign = registry
.get_type_data::<ReflectSubAssign>(TypeId::of::<T>())
.unwrap();
assert_eq!(
reflect_sub
.sub(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&result),
Some(true)
);
reflect_sub_assign.sub_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a.reflect_partial_eq(&result), Some(true));
assert!(reflect_sub.sub(Box::new(a), Box::new("not a T")).is_err());
assert!(reflect_sub.sub(Box::new("not a T"), Box::new(b)).is_err());
assert!(reflect_sub_assign
.sub_assign(&mut a, Box::new("not a T"))
.is_err());
assert!(reflect_sub_assign
.sub_assign(&mut "not a T", Box::new(b))
.is_err());
}
let registry = TypeRegistry::new();
check_sub::<u8>(&registry, 10, 5, 5);
check_sub::<u16>(&registry, 10, 5, 5);
check_sub::<u32>(&registry, 10, 5, 5);
check_sub::<u64>(&registry, 10, 5, 5);
check_sub::<u128>(&registry, 10, 5, 5);
check_sub::<usize>(&registry, 10, 5, 5);
check_sub::<i8>(&registry, 10, 5, 5);
check_sub::<i16>(&registry, 10, 5, 5);
check_sub::<i32>(&registry, 10, 5, 5);
check_sub::<i64>(&registry, 10, 5, 5);
check_sub::<i128>(&registry, 10, 5, 5);
check_sub::<isize>(&registry, 10, 5, 5);
check_sub::<f32>(&registry, 1.5, 2.5, -1.0);
check_sub::<f64>(&registry, 1.5, 2.5, -1.0);
}
#[test]
fn should_mul() {
fn check_mul<T: Reflect + Mul<Output = T> + Copy + 'static>(
registry: &TypeRegistry,
mut a: T,
b: T,
result: T,
) {
let reflect_mul = registry
.get_type_data::<ReflectMul>(TypeId::of::<T>())
.unwrap();
let reflect_mul_assign = registry
.get_type_data::<ReflectMulAssign>(TypeId::of::<T>())
.unwrap();
assert_eq!(
reflect_mul
.mul(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&result),
Some(true)
);
reflect_mul_assign.mul_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a.reflect_partial_eq(&result), Some(true));
assert!(reflect_mul.mul(Box::new(a), Box::new("not a T")).is_err());
assert!(reflect_mul.mul(Box::new("not a T"), Box::new(b)).is_err());
assert!(reflect_mul_assign
.mul_assign(&mut a, Box::new("not a T"))
.is_err());
assert!(reflect_mul_assign
.mul_assign(&mut "not a T", Box::new(b))
.is_err());
}
let registry = TypeRegistry::new();
check_mul::<u8>(&registry, 10, 5, 50);
check_mul::<u16>(&registry, 10, 5, 50);
check_mul::<u32>(&registry, 10, 5, 50);
check_mul::<u64>(&registry, 10, 5, 50);
check_mul::<u128>(&registry, 10, 5, 50);
check_mul::<usize>(&registry, 10, 5, 50);
check_mul::<i8>(&registry, 10, 5, 50);
check_mul::<i16>(&registry, 10, 5, 50);
check_mul::<i32>(&registry, 10, 5, 50);
check_mul::<i64>(&registry, 10, 5, 50);
check_mul::<i128>(&registry, 10, 5, 50);
check_mul::<isize>(&registry, 10, 5, 50);
check_mul::<f32>(&registry, 5., 2., 10.);
check_mul::<f64>(&registry, 5., 2., 10.);
}
#[test]
fn should_div() {
fn check_div<T: Reflect + Div<Output = T> + Copy + 'static>(
registry: &TypeRegistry,
mut a: T,
b: T,
result: T,
) {
let reflect_div = registry
.get_type_data::<ReflectDiv>(TypeId::of::<T>())
.unwrap();
let reflect_div_assign = registry
.get_type_data::<ReflectDivAssign>(TypeId::of::<T>())
.unwrap();
assert_eq!(
reflect_div
.div(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&result),
Some(true)
);
reflect_div_assign.div_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a.reflect_partial_eq(&result), Some(true));
assert!(reflect_div.div(Box::new(a), Box::new("not a T")).is_err());
assert!(reflect_div.div(Box::new("not a T"), Box::new(b)).is_err());
assert!(reflect_div_assign
.div_assign(&mut a, Box::new("not a T"))
.is_err());
assert!(reflect_div_assign
.div_assign(&mut "not a T", Box::new(b))
.is_err());
}
let registry = TypeRegistry::new();
check_div::<u8>(&registry, 10, 5, 2);
check_div::<u16>(&registry, 10, 5, 2);
check_div::<u32>(&registry, 10, 5, 2);
check_div::<u64>(&registry, 10, 5, 2);
check_div::<u128>(&registry, 10, 5, 2);
check_div::<usize>(&registry, 10, 5, 2);
check_div::<i8>(&registry, 10, 5, 2);
check_div::<i16>(&registry, 10, 5, 2);
check_div::<i32>(&registry, 10, 5, 2);
check_div::<i64>(&registry, 10, 5, 2);
check_div::<i128>(&registry, 10, 5, 2);
check_div::<isize>(&registry, 10, 5, 2);
check_div::<f32>(&registry, 10., 2., 5.);
check_div::<f64>(&registry, 10., 2., 5.);
}
#[test]
fn should_rem() {
fn check_rem<T: Reflect + Rem<Output = T> + Copy + 'static>(
registry: &TypeRegistry,
mut a: T,
b: T,
result: T,
) {
let reflect_rem = registry
.get_type_data::<ReflectRem>(TypeId::of::<T>())
.unwrap();
let reflect_rem_assign = registry
.get_type_data::<ReflectRemAssign>(TypeId::of::<T>())
.unwrap();
assert_eq!(
reflect_rem
.rem(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&result),
Some(true)
);
reflect_rem_assign.rem_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a.reflect_partial_eq(&result), Some(true));
assert!(reflect_rem.rem(Box::new(a), Box::new("not a T")).is_err());
assert!(reflect_rem.rem(Box::new("not a T"), Box::new(b)).is_err());
assert!(reflect_rem_assign
.rem_assign(&mut a, Box::new("not a T"))
.is_err());
assert!(reflect_rem_assign
.rem_assign(&mut "not a T", Box::new(b))
.is_err());
}
let registry = TypeRegistry::new();
check_rem::<u8>(&registry, 10, 5, 0);
check_rem::<u16>(&registry, 10, 5, 0);
check_rem::<u32>(&registry, 10, 5, 0);
check_rem::<u64>(&registry, 10, 5, 0);
check_rem::<u128>(&registry, 10, 5, 0);
check_rem::<usize>(&registry, 10, 5, 0);
check_rem::<i8>(&registry, 10, 5, 0);
check_rem::<i16>(&registry, 10, 5, 0);
check_rem::<i32>(&registry, 10, 5, 0);
check_rem::<i64>(&registry, 10, 5, 0);
check_rem::<i128>(&registry, 10, 5, 0);
check_rem::<isize>(&registry, 10, 5, 0);
check_rem::<f32>(&registry, 10., 5., 0.);
check_rem::<f64>(&registry, 10., 5., 0.);
}
}
+54 -4
View File
@@ -1,5 +1,5 @@
use crate::{
std_traits::ReflectDefault,
std_traits::{ReflectAdd, ReflectAddAssign, ReflectDefault, ReflectSub, ReflectSubAssign},
type_registry::{ReflectDeserialize, ReflectSerialize},
};
use bevy_reflect_derive::impl_reflect_opaque;
@@ -11,13 +11,20 @@ impl_reflect_opaque!(::core::time::Duration(
PartialEq,
Serialize,
Deserialize,
Default
Default,
Add,
AddAssign,
Sub,
SubAssign,
));
#[cfg(test)]
mod tests {
use alloc::boxed::Box;
use bevy_reflect::{ReflectSerialize, TypeRegistry};
use core::time::Duration;
use core::{any::TypeId, time::Duration};
use crate::prelude::{ReflectAdd, ReflectAddAssign, ReflectSub, ReflectSubAssign};
#[test]
fn can_serialize_duration() {
@@ -25,8 +32,51 @@ mod tests {
type_registry.register::<Duration>();
let reflect_serialize = type_registry
.get_type_data::<ReflectSerialize>(core::any::TypeId::of::<Duration>())
.get_type_data::<ReflectSerialize>(TypeId::of::<Duration>())
.unwrap();
let _serializable = reflect_serialize.get_serializable(&Duration::ZERO);
}
#[test]
fn should_math_ops_duration() {
let mut registry = TypeRegistry::new();
registry.register::<Duration>();
let reflect_add = registry
.get_type_data::<ReflectAdd>(TypeId::of::<Duration>())
.unwrap();
let reflect_add_assign = registry
.get_type_data::<ReflectAddAssign>(TypeId::of::<Duration>())
.unwrap();
let reflect_sub = registry
.get_type_data::<ReflectSub>(TypeId::of::<Duration>())
.unwrap();
let reflect_sub_assign = registry
.get_type_data::<ReflectSubAssign>(TypeId::of::<Duration>())
.unwrap();
let mut a = Duration::from_secs(10);
let b = Duration::from_secs(4);
assert_eq!(
reflect_add
.add(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&Duration::from_secs(14)),
Some(true)
);
assert_eq!(
reflect_sub
.sub(Box::new(a), Box::new(b))
.unwrap()
.reflect_partial_eq(&Duration::from_secs(6)),
Some(true)
);
reflect_add_assign.add_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a, Duration::from_secs(14));
reflect_sub_assign.sub_assign(&mut a, Box::new(b)).unwrap();
assert_eq!(a, Duration::from_secs(10));
}
}
+444 -1
View File
@@ -1,7 +1,9 @@
//! Module containing the [`ReflectDefault`] type.
use crate::{FromType, Reflect};
use alloc::boxed::Box;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
use crate::{FromType, Reflect};
/// A struct used to provide the default value of a type.
///
@@ -25,3 +27,444 @@ impl<T: Reflect + Default> FromType<T> for ReflectDefault {
}
}
}
/// A struct used to perform addition on reflected values.
///
/// A [`ReflectAdd`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectAdd {
/// Function pointer implementing [`ReflectAdd::add()`].
pub add: fn(
Box<dyn Reflect>,
Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)>,
}
impl ReflectAdd {
/// Adds two reflected values together, returning the result as a new reflected value.
///
/// # Errors
///
/// Returns `Err((a, b))` if the types are incompatible.
pub fn add(
&self,
a: Box<dyn Reflect>,
b: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)> {
(self.add)(a, b)
}
}
impl<T: Reflect + Add<Output: Reflect>> FromType<T> for ReflectAdd {
fn from_type() -> Self {
ReflectAdd {
add: |a: Box<dyn Reflect>, b: Box<dyn Reflect>| {
let (a, b) = match (a.downcast::<T>(), b.downcast::<T>()) {
(Ok(a), Ok(b)) => (a, b),
(a, b) => {
let a = a.map(|a| a as Box<dyn Reflect>).unwrap_or_else(|e| e);
let b = b.map(|b| b as Box<dyn Reflect>).unwrap_or_else(|e| e);
return Err((a, b));
}
};
Ok(Box::new(*a + *b))
},
}
}
}
/// A struct used to perform subtraction on reflected values.
///
/// A [`ReflectSub`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectSub {
/// Function pointer implementing [`ReflectSub::sub()`].
pub sub: fn(
Box<dyn Reflect>,
Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)>,
}
impl ReflectSub {
/// Subtracts two reflected values, returning the result as a new reflected value.
///
/// # Errors
///
/// Returns `Err((a, b))` if the types are incompatible.
pub fn sub(
&self,
a: Box<dyn Reflect>,
b: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)> {
(self.sub)(a, b)
}
}
impl<T: Reflect + Sub<Output: Reflect>> FromType<T> for ReflectSub {
fn from_type() -> Self {
ReflectSub {
sub: |a: Box<dyn Reflect>, b: Box<dyn Reflect>| {
let (a, b) = match (a.downcast::<T>(), b.downcast::<T>()) {
(Ok(a), Ok(b)) => (a, b),
(a, b) => {
let a = a.map(|a| a as Box<dyn Reflect>).unwrap_or_else(|e| e);
let b = b.map(|b| b as Box<dyn Reflect>).unwrap_or_else(|e| e);
return Err((a, b));
}
};
Ok(Box::new(*a - *b))
},
}
}
}
/// A struct used to perform multiplication on reflected values.
///
/// A [`ReflectMul`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectMul {
/// Function pointer implementing [`ReflectMul::mul()`].
pub mul: fn(
Box<dyn Reflect>,
Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)>,
}
impl ReflectMul {
/// Multiplies two reflected values, returning the result as a new reflected value.
///
/// # Errors
///
/// Returns `Err((a, b))` if the types are incompatible.
pub fn mul(
&self,
a: Box<dyn Reflect>,
b: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)> {
(self.mul)(a, b)
}
}
impl<T: Reflect + Mul<Output: Reflect>> FromType<T> for ReflectMul {
fn from_type() -> Self {
ReflectMul {
mul: |a: Box<dyn Reflect>, b: Box<dyn Reflect>| {
let (a, b) = match (a.downcast::<T>(), b.downcast::<T>()) {
(Ok(a), Ok(b)) => (a, b),
(a, b) => {
let a = a.map(|a| a as Box<dyn Reflect>).unwrap_or_else(|e| e);
let b = b.map(|b| b as Box<dyn Reflect>).unwrap_or_else(|e| e);
return Err((a, b));
}
};
Ok(Box::new(*a * *b))
},
}
}
}
/// A struct used to perform division on reflected values.
///
/// A [`ReflectDiv`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectDiv {
/// Function pointer implementing [`ReflectDiv::div()`].
pub div: fn(
Box<dyn Reflect>,
Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)>,
}
impl ReflectDiv {
/// Divides two reflected values, returning the result as a new reflected value.
///
/// # Errors
///
/// Returns `Err((a, b))` if the types are incompatible.
pub fn div(
&self,
a: Box<dyn Reflect>,
b: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)> {
(self.div)(a, b)
}
}
impl<T: Reflect + Div<Output: Reflect>> FromType<T> for ReflectDiv {
fn from_type() -> Self {
ReflectDiv {
div: |a: Box<dyn Reflect>, b: Box<dyn Reflect>| {
let (a, b) = match (a.downcast::<T>(), b.downcast::<T>()) {
(Ok(a), Ok(b)) => (a, b),
(a, b) => {
let a = a.map(|a| a as Box<dyn Reflect>).unwrap_or_else(|e| e);
let b = b.map(|b| b as Box<dyn Reflect>).unwrap_or_else(|e| e);
return Err((a, b));
}
};
Ok(Box::new(*a / *b))
},
}
}
}
/// A struct used to perform division on reflected values.
///
/// A [`ReflectDiv`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectRem {
/// Function pointer implementing [`ReflectRem::rem()`].
pub rem: fn(
Box<dyn Reflect>,
Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)>,
}
impl ReflectRem {
/// Computes the remainder of two reflected values, returning the result as
/// a new reflected value.
///
/// # Errors
///
/// Returns `Err((a, b))` if the types are incompatible.
pub fn rem(
&self,
a: Box<dyn Reflect>,
b: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, (Box<dyn Reflect>, Box<dyn Reflect>)> {
(self.rem)(a, b)
}
}
impl<T: Reflect + Rem<Output: Reflect>> FromType<T> for ReflectRem {
fn from_type() -> Self {
ReflectRem {
rem: |a: Box<dyn Reflect>, b: Box<dyn Reflect>| {
let (a, b) = match (a.downcast::<T>(), b.downcast::<T>()) {
(Ok(a), Ok(b)) => (a, b),
(a, b) => {
let a = a.map(|a| a as Box<dyn Reflect>).unwrap_or_else(|e| e);
let b = b.map(|b| b as Box<dyn Reflect>).unwrap_or_else(|e| e);
return Err((a, b));
}
};
Ok(Box::new(*a % *b))
},
}
}
}
/// A struct used to perform addition assignment on reflected values.
///
/// A [`ReflectAddAssign`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectAddAssign {
/// Function pointer implementing [`ReflectAddAssign::add_assign()`].
pub add_assign: fn(&mut dyn Reflect, Box<dyn Reflect>) -> Result<(), Option<Box<dyn Reflect>>>,
}
impl ReflectAddAssign {
/// Adds a reflected value to another reflected value in place.
///
/// # Errors
///
/// - Returns `Err(None)` if the first argument is of an incompatible type.
/// - Returns `Err(Some(b))` if the second argument is of an incompatible type.
pub fn add_assign(
&self,
a: &mut dyn Reflect,
b: Box<dyn Reflect>,
) -> Result<(), Option<Box<dyn Reflect>>> {
(self.add_assign)(a, b)
}
}
impl<T: Reflect + AddAssign> FromType<T> for ReflectAddAssign {
fn from_type() -> Self {
ReflectAddAssign {
add_assign: |a: &mut dyn Reflect, b: Box<dyn Reflect>| {
let Some(a) = a.downcast_mut::<T>() else {
return Err(None);
};
let b = match b.downcast::<T>() {
Ok(b) => b,
Err(b) => return Err(Some(b)),
};
*a += *b;
Ok(())
},
}
}
}
/// A struct used to perform subtraction assignment on reflected values.
///
/// A [`ReflectSubAssign`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectSubAssign {
/// Function pointer implementing [`ReflectSubAssign::sub_assign()`].
pub sub_assign: fn(&mut dyn Reflect, Box<dyn Reflect>) -> Result<(), Option<Box<dyn Reflect>>>,
}
impl ReflectSubAssign {
/// Subtracts a reflected value from another reflected value in place.
///
/// # Errors
///
/// - Returns `Err(None)` if the first argument is of an incompatible type.
/// - Returns `Err(Some(b))` if the second argument is of an incompatible type.
pub fn sub_assign(
&self,
a: &mut dyn Reflect,
b: Box<dyn Reflect>,
) -> Result<(), Option<Box<dyn Reflect>>> {
(self.sub_assign)(a, b)
}
}
impl<T: Reflect + SubAssign> FromType<T> for ReflectSubAssign {
fn from_type() -> Self {
ReflectSubAssign {
sub_assign: |a: &mut dyn Reflect, b: Box<dyn Reflect>| {
let Some(a) = a.downcast_mut::<T>() else {
return Err(None);
};
let b = match b.downcast::<T>() {
Ok(b) => b,
Err(b) => return Err(Some(b)),
};
*a -= *b;
Ok(())
},
}
}
}
/// A struct used to perform multiplication assignment on reflected values.
///
/// A [`ReflectMulAssign`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectMulAssign {
/// Function pointer implementing [`ReflectMulAssign::mul_assign()`].
pub mul_assign: fn(&mut dyn Reflect, Box<dyn Reflect>) -> Result<(), Option<Box<dyn Reflect>>>,
}
impl ReflectMulAssign {
/// Multiplies a reflected value by another reflected value in place.
///
/// # Errors
///
/// - Returns `Err(None)` if the first argument is of an incompatible type.
/// - Returns `Err(Some(b))` if the second argument is of an incompatible type.
pub fn mul_assign(
&self,
a: &mut dyn Reflect,
b: Box<dyn Reflect>,
) -> Result<(), Option<Box<dyn Reflect>>> {
(self.mul_assign)(a, b)
}
}
impl<T: Reflect + MulAssign> FromType<T> for ReflectMulAssign {
fn from_type() -> Self {
ReflectMulAssign {
mul_assign: |a: &mut dyn Reflect, b: Box<dyn Reflect>| {
let Some(a) = a.downcast_mut::<T>() else {
return Err(None);
};
let b = match b.downcast::<T>() {
Ok(b) => b,
Err(b) => return Err(Some(b)),
};
*a *= *b;
Ok(())
},
}
}
}
/// A struct used to perform division assignment on reflected values.
///
/// A [`ReflectDivAssign`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectDivAssign {
/// Function pointer implementing [`ReflectDivAssign::div_assign()`].
pub div_assign: fn(&mut dyn Reflect, Box<dyn Reflect>) -> Result<(), Option<Box<dyn Reflect>>>,
}
impl ReflectDivAssign {
/// Divides a reflected value by another reflected value in place.
///
/// # Errors
///
/// - Returns `Err(None)` if the first argument is of an incompatible type.
/// - Returns `Err(Some(b))` if the second argument is of an incompatible type.
pub fn div_assign(
&self,
a: &mut dyn Reflect,
b: Box<dyn Reflect>,
) -> Result<(), Option<Box<dyn Reflect>>> {
(self.div_assign)(a, b)
}
}
impl<T: Reflect + DivAssign> FromType<T> for ReflectDivAssign {
fn from_type() -> Self {
ReflectDivAssign {
div_assign: |a: &mut dyn Reflect, b: Box<dyn Reflect>| {
let Some(a) = a.downcast_mut::<T>() else {
return Err(None);
};
let b = match b.downcast::<T>() {
Ok(b) => b,
Err(b) => return Err(Some(b)),
};
*a /= *b;
Ok(())
},
}
}
}
/// A struct used to perform remainder assignment on reflected values.
///
/// A [`ReflectRemAssign`] for type `T` can be obtained via [`FromType::from_type`].
#[derive(Clone)]
pub struct ReflectRemAssign {
/// Function pointer implementing [`ReflectRemAssign::rem_assign()`].
pub rem_assign: fn(&mut dyn Reflect, Box<dyn Reflect>) -> Result<(), Option<Box<dyn Reflect>>>,
}
impl ReflectRemAssign {
/// Computes the remainder of a reflected value by another reflected value in place.
///
/// # Errors
///
/// - Returns `Err(None)` if the first argument is of an incompatible type.
/// - Returns `Err(Some(b))` if the second argument is of an incompatible type.
pub fn rem_assign(
&self,
a: &mut dyn Reflect,
b: Box<dyn Reflect>,
) -> Result<(), Option<Box<dyn Reflect>>> {
(self.rem_assign)(a, b)
}
}
impl<T: Reflect + RemAssign> FromType<T> for ReflectRemAssign {
fn from_type() -> Self {
ReflectRemAssign {
rem_assign: |a: &mut dyn Reflect, b: Box<dyn Reflect>| {
let Some(a) = a.downcast_mut::<T>() else {
return Err(None);
};
let b = match b.downcast::<T>() {
Ok(b) => b,
Err(b) => return Err(Some(b)),
};
*a %= *b;
Ok(())
},
}
}
}