use alloc::boxed::Box;
use core::{any::TypeId, marker::PhantomData};
use bevy_utils::TypeIdMap;
use crate::{Reflect, TypePath};
#[derive(Default)]
pub struct ReflectConvert {
conversions: TypeIdMap<Box<dyn Converter>>,
}
trait Converter: Send + Sync {
fn convert(&self, input: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>>;
fn clone_converter(&self) -> Box<dyn Converter>;
}
struct TypedConverter<T, U, F>
where
T: Reflect + TypePath,
U: Reflect + TypePath,
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
{
function: F,
phantom: PhantomData<(T, U)>,
}
impl ReflectConvert {
pub fn try_convert_from(
&self,
input: Box<dyn Reflect>,
) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
let type_id = (*input.as_any()).type_id();
match self.conversions.get(&type_id) {
Some(converter) => converter.convert(input),
None => Err(input),
}
}
pub fn register_type_conversion<T, U, F>(&mut self, function: F)
where
T: Reflect + TypePath,
U: Reflect + TypePath,
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
{
self.conversions.insert(
TypeId::of::<T>(),
Box::new(TypedConverter {
function,
phantom: PhantomData,
}),
);
}
}
impl Clone for ReflectConvert {
fn clone(&self) -> Self {
ReflectConvert {
conversions: self
.conversions
.iter()
.map(|(type_id, converter)| (*type_id, converter.clone_converter()))
.collect(),
}
}
}
impl<T, U, F> Clone for TypedConverter<T, U, F>
where
T: Reflect + TypePath,
U: Reflect + TypePath,
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
{
fn clone(&self) -> Self {
TypedConverter {
function: self.function.clone(),
phantom: PhantomData,
}
}
}
impl<T, U, F> Converter for TypedConverter<T, U, F>
where
T: Reflect + TypePath,
U: Reflect + TypePath,
F: Fn(T) -> Result<U, T> + Clone + Send + Sync + 'static,
{
fn convert(&self, input: Box<dyn Reflect>) -> Result<Box<dyn Reflect>, Box<dyn Reflect>> {
let mut input = input.downcast::<T>()?;
match (self.function)(*input) {
Ok(value) => Ok(Box::new(value)),
Err(value) => {
*input = value;
Err(input)
}
}
}
fn clone_converter(&self) -> Box<dyn Converter> {
Box::new(self.clone())
}
}
#[cfg(test)]
mod tests {
use alloc::{
borrow::ToOwned as _,
boxed::Box,
string::{String, ToString},
};
use core::any::TypeId;
use crate::{convert::ReflectConvert, type_registry::GetTypeRegistration, TypeRegistry};
#[test]
fn convert_from_i32_to_string() {
let mut registry = TypeRegistry::default();
registry.add_registration(i32::get_type_registration());
registry.add_registration(String::get_type_registration());
registry.register_type_conversion(|x: i32| Ok(x.to_string()));
let reflect_convert = registry
.get_type_data::<ReflectConvert>(TypeId::of::<String>())
.unwrap();
let converted = reflect_convert
.try_convert_from(Box::new(12345i32))
.unwrap()
.downcast::<String>()
.unwrap();
assert_eq!(&**converted, "12345");
}
#[test]
fn convert_from_string_to_i32() {
let mut registry = TypeRegistry::default();
registry.add_registration(i32::get_type_registration());
registry.add_registration(String::get_type_registration());
registry.register_type_conversion(|x: String| match x.parse::<i32>() {
Ok(value) => Ok(value),
Err(_) => Err(x),
});
let reflect_convert = registry
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
.unwrap();
let converted = reflect_convert
.try_convert_from(Box::new("12345".to_owned()))
.unwrap()
.downcast::<i32>()
.unwrap();
assert_eq!(*converted, 12345);
let error = reflect_convert
.try_convert_from(Box::new("qqqqq".to_owned()))
.unwrap_err()
.downcast::<String>()
.unwrap();
assert_eq!(&**error, "qqqqq");
}
#[test]
fn convert_from_f32_and_u32_to_i32() {
let mut registry = TypeRegistry::default();
registry.add_registration(i32::get_type_registration());
registry.add_registration(f32::get_type_registration());
registry.add_registration(u32::get_type_registration());
registry.register_type_conversion::<u32, i32, _>(|n: u32| n.try_into().map_err(|_| n));
registry.register_type_conversion::<f32, i32, _>(|n: f32| Ok(n as i32));
let reflect_convert = registry
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
.unwrap();
let a = reflect_convert
.try_convert_from(Box::new(99u32))
.unwrap()
.downcast::<i32>()
.unwrap();
assert_eq!(*a, 99i32);
let b = reflect_convert
.try_convert_from(Box::new(99.0f32))
.unwrap()
.downcast::<i32>()
.unwrap();
assert_eq!(*b, 99i32);
}
#[test]
fn no_such_conversion() {
let mut registry = TypeRegistry::default();
registry.add_registration(i32::get_type_registration());
registry.add_registration(String::get_type_registration());
registry
.get_mut(TypeId::of::<i32>())
.unwrap()
.insert(ReflectConvert::default());
let reflect_convert = registry
.get_type_data::<ReflectConvert>(TypeId::of::<i32>())
.unwrap();
let error = reflect_convert
.try_convert_from(Box::new("12345".to_owned()))
.unwrap_err()
.downcast::<String>()
.unwrap();
assert_eq!(&**error, "12345");
}
}