mirror of
https://github.com/bevyengine/bevy.git
synced 2026-05-06 06:06:42 -04:00
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:
@@ -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>(®istry, 10, 5, 15);
|
||||
check_add::<u16>(®istry, 10, 5, 15);
|
||||
check_add::<u32>(®istry, 10, 5, 15);
|
||||
check_add::<u64>(®istry, 10, 5, 15);
|
||||
check_add::<u128>(®istry, 10, 5, 15);
|
||||
check_add::<usize>(®istry, 10, 5, 15);
|
||||
check_add::<i8>(®istry, 10, 5, 15);
|
||||
check_add::<i16>(®istry, 10, 5, 15);
|
||||
check_add::<i32>(®istry, 10, 5, 15);
|
||||
check_add::<i64>(®istry, 10, 5, 15);
|
||||
check_add::<i128>(®istry, 10, 5, 15);
|
||||
check_add::<isize>(®istry, 10, 5, 15);
|
||||
check_add::<f32>(®istry, 1.5, 2.5, 4.0);
|
||||
check_add::<f64>(®istry, 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>(®istry, 10, 5, 5);
|
||||
check_sub::<u16>(®istry, 10, 5, 5);
|
||||
check_sub::<u32>(®istry, 10, 5, 5);
|
||||
check_sub::<u64>(®istry, 10, 5, 5);
|
||||
check_sub::<u128>(®istry, 10, 5, 5);
|
||||
check_sub::<usize>(®istry, 10, 5, 5);
|
||||
check_sub::<i8>(®istry, 10, 5, 5);
|
||||
check_sub::<i16>(®istry, 10, 5, 5);
|
||||
check_sub::<i32>(®istry, 10, 5, 5);
|
||||
check_sub::<i64>(®istry, 10, 5, 5);
|
||||
check_sub::<i128>(®istry, 10, 5, 5);
|
||||
check_sub::<isize>(®istry, 10, 5, 5);
|
||||
check_sub::<f32>(®istry, 1.5, 2.5, -1.0);
|
||||
check_sub::<f64>(®istry, 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>(®istry, 10, 5, 50);
|
||||
check_mul::<u16>(®istry, 10, 5, 50);
|
||||
check_mul::<u32>(®istry, 10, 5, 50);
|
||||
check_mul::<u64>(®istry, 10, 5, 50);
|
||||
check_mul::<u128>(®istry, 10, 5, 50);
|
||||
check_mul::<usize>(®istry, 10, 5, 50);
|
||||
check_mul::<i8>(®istry, 10, 5, 50);
|
||||
check_mul::<i16>(®istry, 10, 5, 50);
|
||||
check_mul::<i32>(®istry, 10, 5, 50);
|
||||
check_mul::<i64>(®istry, 10, 5, 50);
|
||||
check_mul::<i128>(®istry, 10, 5, 50);
|
||||
check_mul::<isize>(®istry, 10, 5, 50);
|
||||
check_mul::<f32>(®istry, 5., 2., 10.);
|
||||
check_mul::<f64>(®istry, 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>(®istry, 10, 5, 2);
|
||||
check_div::<u16>(®istry, 10, 5, 2);
|
||||
check_div::<u32>(®istry, 10, 5, 2);
|
||||
check_div::<u64>(®istry, 10, 5, 2);
|
||||
check_div::<u128>(®istry, 10, 5, 2);
|
||||
check_div::<usize>(®istry, 10, 5, 2);
|
||||
check_div::<i8>(®istry, 10, 5, 2);
|
||||
check_div::<i16>(®istry, 10, 5, 2);
|
||||
check_div::<i32>(®istry, 10, 5, 2);
|
||||
check_div::<i64>(®istry, 10, 5, 2);
|
||||
check_div::<i128>(®istry, 10, 5, 2);
|
||||
check_div::<isize>(®istry, 10, 5, 2);
|
||||
check_div::<f32>(®istry, 10., 2., 5.);
|
||||
check_div::<f64>(®istry, 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>(®istry, 10, 5, 0);
|
||||
check_rem::<u16>(®istry, 10, 5, 0);
|
||||
check_rem::<u32>(®istry, 10, 5, 0);
|
||||
check_rem::<u64>(®istry, 10, 5, 0);
|
||||
check_rem::<u128>(®istry, 10, 5, 0);
|
||||
check_rem::<usize>(®istry, 10, 5, 0);
|
||||
check_rem::<i8>(®istry, 10, 5, 0);
|
||||
check_rem::<i16>(®istry, 10, 5, 0);
|
||||
check_rem::<i32>(®istry, 10, 5, 0);
|
||||
check_rem::<i64>(®istry, 10, 5, 0);
|
||||
check_rem::<i128>(®istry, 10, 5, 0);
|
||||
check_rem::<isize>(®istry, 10, 5, 0);
|
||||
check_rem::<f32>(®istry, 10., 5., 0.);
|
||||
check_rem::<f64>(®istry, 10., 5., 0.);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user