lang-extension 1.3.0

Rust Lang Extension
Documentation
use std::any::{TypeId, type_name};
use std::fmt;
use std::sync::Arc;

use crate::any::*;
use crate::ops::function::*;
use crate::*;

pub trait RawTypeConverter: Value + Sync + Send {
    fn get_source_type(&self) -> TypeId;

    fn get_target_type(&self) -> TypeId;

    fn convert_raw(&self, source: &dyn Value) -> Result<Box<dyn Value>, Box<dyn Value>>;

    as_trait!(RawTypeConverter);
    as_boxed!(RawTypeConverter);
}

boxed_value_trait!(RawTypeConverter);

pub trait TypeConverter<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint>:
    RawTypeConverter
{
    fn convert(&self, source: &S) -> Result<Box<T>, Box<dyn Value>>;

    as_boxed!(TypeConverter<S, T>);
}

boxed_value_trait!(TypeConverter<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint>);

#[macro_export]
macro_rules! raw_type_converter {
    ($source_type: ty, $target_type: ty) => {
        fn get_source_type(&self) -> TypeId {
            TypeId::of::<$source_type>()
        }

        fn get_target_type(&self) -> TypeId {
            TypeId::of::<$target_type>()
        }

        fn convert_raw(&self, source: &dyn Value) -> Result<Box<dyn Value>, Box<dyn Value>> {
            match source.as_any_ref().downcast_ref::<$source_type>() {
                Some(s) => match $crate::convert::TypeConverter::convert(self, s) {
                    Ok(t) => Ok(Value::to_boxed(*t)),
                    Err(err) => Err(err),
                },
                None => Err(Box::new(format!(
                    "source value {:?} is of an unsupported type: {:?}, only support: {:?}",
                    source,
                    source.type_name(),
                    std::any::type_name::<$source_type>()
                ))),
            }
        }

        as_trait!(impl RawTypeConverter);
        as_boxed!(impl RawTypeConverter);
    };

    ($type: ty, $source_type: ty, $target_type: ty) => {
        impl $crate::convert::RawTypeConverter for $type {
            raw_type_converter!($source_type, $target_type);
        }

        unsafe impl Sync for $type {}
        unsafe impl Send for $type {}
    };

    ($type: tt<$source_type:tt, $target_type:tt>) => {
        impl<$source_type: ?Sized + ValueConstraint, $target_type: ?Sized + ValueConstraint>
            $crate::convert::RawTypeConverter for $type<$source_type, $target_type>
        {
            raw_type_converter!($source_type, $target_type);
        }

        unsafe impl<$source_type: ?Sized + ValueConstraint, $target_type: ?Sized + ValueConstraint>
            Sync for $type<$source_type, $target_type>
        {
        }
        unsafe impl<$source_type: ?Sized + ValueConstraint, $target_type: ?Sized + ValueConstraint>
            Send for $type<$source_type, $target_type>
        {
        }
    };
}

#[derive(Clone)]
pub struct DefaultTypeConverter<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> {
    convert: FunctionRef<S, Result<Box<T>, Box<dyn Value>>>,
}

impl<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> DefaultTypeConverter<S, T> {
    pub fn new(convert: Box<dyn Fn(&S) -> Result<Box<T>, Box<dyn Value>>>) -> Self {
        Self::wrap(Arc::new(convert))
    }

    pub fn wrap(convert: FunctionRef<S, Result<Box<T>, Box<dyn Value>>>) -> Self {
        Self { convert: convert }
    }
}

impl<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> TypeConverter<S, T>
    for DefaultTypeConverter<S, T>
{
    fn convert(&self, source: &S) -> Result<Box<T>, Box<dyn Value>> {
        self.convert.as_ref()(source)
    }

    as_boxed!(impl TypeConverter<S, T>);
}

impl<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> PartialEq
    for DefaultTypeConverter<S, T>
{
    fn eq(&self, other: &Self) -> bool {
        self.convert
            .as_ref()
            .reference_equals(other.convert.as_ref())
    }
}

impl<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> Eq for DefaultTypeConverter<S, T> {}

impl<S: ?Sized + ValueConstraint, T: ?Sized + ValueConstraint> fmt::Debug
    for DefaultTypeConverter<S, T>
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} {{ source_type: {}, target_type: {}, converter: {} }}",
            type_name::<DefaultTypeConverter<S, T>>(),
            type_name::<S>(),
            type_name::<T>(),
            type_name::<FunctionRef<S, Result<T, Box<dyn Value>>>>()
        )
    }
}

raw_type_converter!(DefaultTypeConverter<S, T>);

pub type InplaceTypeConverter<V> = dyn TypeConverter<V, V>;

pub fn new_inplace_type_converter<V: ?Sized + ValueConstraint>() -> Box<InplaceTypeConverter<V>> {
    TypeConverter::to_boxed(DefaultTypeConverter::<V, V>::new(Box::new(|v| {
        Ok(Box::new(v.clone()))
    })))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(Hash, PartialEq, Eq, Debug, Clone)]
    struct C;

    impl TypeConverter<i32, String> for C {
        fn convert(&self, source: &i32) -> Result<Box<String>, Box<dyn Value>> {
            Ok(Box::new(source.to_string()))
        }

        as_boxed!(impl TypeConverter<i32, String>);
    }

    raw_type_converter!(C, i32, String);

    #[test]
    fn type_convert() {
        match C.convert(&9) {
            Ok(s) => {
                println!("{}", s);
                assert_eq!("9".to_string(), *s);
            }
            Err(err) => {
                println!("{:?}", err);
                assert!(false);
            }
        }

        match C.convert_raw(Value::as_trait_ref(&9)) {
            Ok(s) => {
                println!("{:?}", s);
                assert_eq!(
                    "9".to_string(),
                    *s.as_ref().as_any_ref().downcast_ref::<String>().unwrap()
                );
            }
            Err(err) => {
                println!("{:?}", err);
                assert!(false);
            }
        }

        match C.convert_raw(Value::as_trait_ref(&"ok")) {
            Ok(s) => {
                println!("{:?}", s);
                assert!(false);
            }
            Err(err) => {
                println!("{:?}", err);
            }
        }
    }
}