//! Helper module which defines the `Any` trait to to allow dynamic value handling. use crate::fn_native::SendSync; use crate::module::Module; use crate::parser::{ImmutableString, INT}; use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast}; #[cfg(not(feature = "no_float"))] use crate::parser::FLOAT; #[cfg(not(feature = "no_index"))] use crate::engine::Array; #[cfg(not(feature = "no_object"))] use crate::engine::Map; use crate::stdlib::{ any::{type_name, Any, TypeId}, boxed::Box, collections::HashMap, fmt, string::String, vec::Vec, }; #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] use crate::stdlib::time::Instant; #[cfg(not(feature = "no_std"))] #[cfg(target_arch = "wasm32")] use instant::Instant; /// Trait to represent any type. /// /// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type. /// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`. #[cfg(not(feature = "sync"))] pub trait Variant: Any { /// Convert this `Variant` trait object to `&dyn Any`. fn as_any(&self) -> &dyn Any; /// Convert this `Variant` trait object to `&mut dyn Any`. fn as_mut_any(&mut self) -> &mut dyn Any; /// Convert this `Variant` trait object to an `Any` trait object. fn as_box_any(self: Box) -> Box; /// Get the name of this type. fn type_name(&self) -> &'static str; /// Convert into `Dynamic`. fn into_dynamic(self) -> Dynamic; /// Clone into `Dynamic`. fn clone_into_dynamic(&self) -> Dynamic; /// This trait may only be implemented by `rhai`. #[doc(hidden)] fn _closed(&self) -> _Private; } /// Trait to represent any type. /// /// `From<_>` is implemented for `i64` (`i32` if `only_i32`), `f64` (if not `no_float`), /// `bool`, `String`, `char`, `Vec` (into `Array`) and `HashMap` (into `Map`). #[cfg(feature = "sync")] pub trait Variant: Any + Send + Sync { /// Convert this `Variant` trait object to `&dyn Any`. fn as_any(&self) -> &dyn Any; /// Convert this `Variant` trait object to `&mut dyn Any`. fn as_mut_any(&mut self) -> &mut dyn Any; /// Convert this `Variant` trait object to an `Any` trait object. fn as_box_any(self: Box) -> Box; /// Get the name of this type. fn type_name(&self) -> &'static str; /// Convert into `Dynamic`. fn into_dynamic(self) -> Dynamic; /// Clone into `Dynamic`. fn clone_into_dynamic(&self) -> Dynamic; /// This trait may only be implemented by `rhai`. #[doc(hidden)] fn _closed(&self) -> _Private; } impl Variant for T { fn as_any(&self) -> &dyn Any { self } fn as_mut_any(&mut self) -> &mut dyn Any { self } fn as_box_any(self: Box) -> Box { self } fn type_name(&self) -> &'static str { type_name::() } fn into_dynamic(self) -> Dynamic { Dynamic::from(self) } fn clone_into_dynamic(&self) -> Dynamic { Dynamic::from(self.clone()) } fn _closed(&self) -> _Private { _Private } } impl dyn Variant { /// Is this `Variant` a specific type? pub fn is(&self) -> bool { TypeId::of::() == self.type_id() } } /// Dynamic type containing any value. pub struct Dynamic(pub(crate) Union); /// Internal `Dynamic` representation. /// /// Most variants are boxed to reduce the size. pub enum Union { Unit(()), Bool(bool), Str(ImmutableString), Char(char), Int(INT), #[cfg(not(feature = "no_float"))] Float(FLOAT), #[cfg(not(feature = "no_index"))] Array(Box), #[cfg(not(feature = "no_object"))] Map(Box), Module(Box), Variant(Box>), } impl Dynamic { /// Does this `Dynamic` hold a variant data type /// instead of one of the support system primitive types? pub fn is_variant(&self) -> bool { match self.0 { Union::Variant(_) => true, _ => false, } } /// Is the value held by this `Dynamic` a particular type? pub fn is(&self) -> bool { self.type_id() == TypeId::of::() || match self.0 { Union::Str(_) => TypeId::of::() == TypeId::of::(), _ => false, } } /// Get the TypeId of the value held by this `Dynamic`. pub fn type_id(&self) -> TypeId { match &self.0 { Union::Unit(_) => TypeId::of::<()>(), Union::Bool(_) => TypeId::of::(), Union::Str(_) => TypeId::of::(), Union::Char(_) => TypeId::of::(), Union::Int(_) => TypeId::of::(), #[cfg(not(feature = "no_float"))] Union::Float(_) => TypeId::of::(), #[cfg(not(feature = "no_index"))] Union::Array(_) => TypeId::of::(), #[cfg(not(feature = "no_object"))] Union::Map(_) => TypeId::of::(), Union::Module(_) => TypeId::of::(), Union::Variant(value) => (***value).type_id(), } } /// Get the name of the type of the value held by this `Dynamic`. pub fn type_name(&self) -> &'static str { match &self.0 { Union::Unit(_) => "()", Union::Bool(_) => "bool", Union::Str(_) => "string", Union::Char(_) => "char", Union::Int(_) => type_name::(), #[cfg(not(feature = "no_float"))] Union::Float(_) => type_name::(), #[cfg(not(feature = "no_index"))] Union::Array(_) => "array", #[cfg(not(feature = "no_object"))] Union::Map(_) => "map", Union::Module(_) => "sub-scope", #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] Union::Variant(value) if value.is::() => "timestamp", Union::Variant(value) => (***value).type_name(), } } } impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { Union::Unit(_) => write!(f, ""), Union::Bool(value) => fmt::Display::fmt(value, f), Union::Str(value) => fmt::Display::fmt(value, f), Union::Char(value) => fmt::Display::fmt(value, f), Union::Int(value) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_float"))] Union::Float(value) => fmt::Display::fmt(value, f), #[cfg(not(feature = "no_index"))] Union::Array(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), Union::Module(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] Union::Variant(value) if value.is::() => write!(f, ""), Union::Variant(_) => write!(f, "?"), } } } impl fmt::Debug for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { Union::Unit(value) => fmt::Debug::fmt(value, f), Union::Bool(value) => fmt::Debug::fmt(value, f), Union::Str(value) => fmt::Debug::fmt(value, f), Union::Char(value) => fmt::Debug::fmt(value, f), Union::Int(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_float"))] Union::Float(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_index"))] Union::Array(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_object"))] Union::Map(value) => write!(f, "#{:?}", value), Union::Module(value) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_std"))] #[cfg(not(target_arch = "wasm32"))] Union::Variant(value) if value.is::() => write!(f, ""), Union::Variant(_) => write!(f, ""), } } } impl Clone for Dynamic { fn clone(&self) -> Self { match self.0 { Union::Unit(value) => Self(Union::Unit(value)), Union::Bool(value) => Self(Union::Bool(value)), Union::Str(ref value) => Self(Union::Str(value.clone())), Union::Char(value) => Self(Union::Char(value)), Union::Int(value) => Self(Union::Int(value)), #[cfg(not(feature = "no_float"))] Union::Float(value) => Self(Union::Float(value)), #[cfg(not(feature = "no_index"))] Union::Array(ref value) => Self(Union::Array(value.clone())), #[cfg(not(feature = "no_object"))] Union::Map(ref value) => Self(Union::Map(value.clone())), Union::Module(ref value) => Self(Union::Module(value.clone())), Union::Variant(ref value) => (***value).clone_into_dynamic(), } } } impl Default for Dynamic { fn default() -> Self { Self(Union::Unit(())) } } impl Dynamic { /// Create a `Dynamic` from any type. A `Dynamic` value is simply returned as is. /// /// # Safety /// /// This type uses some unsafe code, mainly for type casting. /// /// # Notes /// /// Beware that you need to pass in an `Array` type for it to be recognized as an `Array`. /// A `Vec` does not get automatically converted to an `Array`, but will be a generic /// restricted trait object instead, because `Vec` is not a supported standard type. /// /// Similarly, passing in a `HashMap` will not get a `Map` but a trait object. /// /// # Examples /// /// ``` /// use rhai::Dynamic; /// /// let result = Dynamic::from(42_i64); /// assert_eq!(result.type_name(), "i64"); /// assert_eq!(result.to_string(), "42"); /// /// let result = Dynamic::from("hello".to_string()); /// assert_eq!(result.type_name(), "string"); /// assert_eq!(result.to_string(), "hello"); /// /// let new_result = Dynamic::from(result); /// assert_eq!(new_result.type_name(), "string"); /// assert_eq!(new_result.to_string(), "hello"); /// ``` pub fn from(value: T) -> Self { if let Some(result) = ::downcast_ref::<()>(&value) { return result.clone().into(); } else if let Some(result) = ::downcast_ref::(&value) { return result.clone().into(); } else if let Some(result) = ::downcast_ref::(&value) { return result.clone().into(); } else if let Some(result) = ::downcast_ref::(&value) { return result.clone().into(); } else if let Some(result) = ::downcast_ref::(&value) { return result.clone().into(); } #[cfg(not(feature = "no_float"))] { if let Some(result) = ::downcast_ref::(&value) { return result.clone().into(); } } let mut boxed = Box::new(value); boxed = match unsafe_cast_box::<_, Dynamic>(boxed) { Ok(d) => return *d, Err(val) => val, }; boxed = match unsafe_cast_box::<_, String>(boxed) { Ok(s) => return (*s).into(), Err(val) => val, }; #[cfg(not(feature = "no_index"))] { boxed = match unsafe_cast_box::<_, Array>(boxed) { Ok(array) => return (*array).into(), Err(val) => val, }; } #[cfg(not(feature = "no_object"))] { boxed = match unsafe_cast_box::<_, Map>(boxed) { Ok(map) => return (*map).into(), Err(val) => val, } } Self(Union::Variant(Box::new(boxed))) } /// Get a copy of the `Dynamic` value as a specific type. /// Casting to a `Dynamic` just returns as is. /// /// Returns an error with the name of the value's actual type when the cast fails. /// /// # Example /// /// ``` /// use rhai::Dynamic; /// /// let x = Dynamic::from(42_u32); /// /// assert_eq!(x.try_cast::().unwrap(), 42); /// ``` pub fn try_cast(self) -> Option { let type_id = TypeId::of::(); if type_id == TypeId::of::() { return unsafe_cast_box::<_, T>(Box::new(self)).ok().map(|v| *v); } match self.0 { Union::Unit(value) => unsafe_try_cast(value), Union::Bool(value) => unsafe_try_cast(value), Union::Str(value) if type_id == TypeId::of::() => { unsafe_try_cast(value) } Union::Str(value) => unsafe_try_cast(value.into_owned()), Union::Char(value) => unsafe_try_cast(value), Union::Int(value) => unsafe_try_cast(value), #[cfg(not(feature = "no_float"))] Union::Float(value) => unsafe_try_cast(value), #[cfg(not(feature = "no_index"))] Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), #[cfg(not(feature = "no_object"))] Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(), } } /// Get a copy of the `Dynamic` value as a specific type. /// Casting to a `Dynamic` just returns as is. /// /// # Panics /// /// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type). /// /// # Example /// /// ``` /// use rhai::Dynamic; /// /// let x = Dynamic::from(42_u32); /// /// assert_eq!(x.cast::(), 42); /// ``` pub fn cast(self) -> T { let type_id = TypeId::of::(); if type_id == TypeId::of::() { return *unsafe_cast_box::<_, T>(Box::new(self)).unwrap(); } match self.0 { Union::Unit(value) => unsafe_try_cast(value).unwrap(), Union::Bool(value) => unsafe_try_cast(value).unwrap(), Union::Str(value) if type_id == TypeId::of::() => { unsafe_try_cast(value).unwrap() } Union::Str(value) => unsafe_try_cast(value.into_owned()).unwrap(), Union::Char(value) => unsafe_try_cast(value).unwrap(), Union::Int(value) => unsafe_try_cast(value).unwrap(), #[cfg(not(feature = "no_float"))] Union::Float(value) => unsafe_try_cast(value).unwrap(), #[cfg(not(feature = "no_index"))] Union::Array(value) => *unsafe_cast_box::<_, T>(value).unwrap(), #[cfg(not(feature = "no_object"))] Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(), Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(), Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(), } } /// Get a reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a reference to it. /// Returns `None` if the cast fails. pub fn downcast_ref(&self) -> Option<&T> { if TypeId::of::() == TypeId::of::() { return ::downcast_ref::(self); } match &self.0 { Union::Unit(value) => ::downcast_ref::(value), Union::Bool(value) => ::downcast_ref::(value), Union::Str(value) => (value as &dyn Any) .downcast_ref::() .or_else(|| ::downcast_ref::(value.as_ref())), Union::Char(value) => ::downcast_ref::(value), Union::Int(value) => ::downcast_ref::(value), #[cfg(not(feature = "no_float"))] Union::Float(value) => ::downcast_ref::(value), #[cfg(not(feature = "no_index"))] Union::Array(value) => ::downcast_ref::(value.as_ref()), #[cfg(not(feature = "no_object"))] Union::Map(value) => ::downcast_ref::(value.as_ref()), Union::Module(value) => ::downcast_ref::(value.as_ref()), Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::(), } } /// Get a mutable reference of a specific type to the `Dynamic`. /// Casting to `Dynamic` just returns a mutable reference to it. /// Returns `None` if the cast fails. pub fn downcast_mut(&mut self) -> Option<&mut T> { if TypeId::of::() == TypeId::of::() { return ::downcast_mut::(self); } match &mut self.0 { Union::Unit(value) => ::downcast_mut::(value), Union::Bool(value) => ::downcast_mut::(value), Union::Str(value) => ::downcast_mut::(value), Union::Char(value) => ::downcast_mut::(value), Union::Int(value) => ::downcast_mut::(value), #[cfg(not(feature = "no_float"))] Union::Float(value) => ::downcast_mut::(value), #[cfg(not(feature = "no_index"))] Union::Array(value) => ::downcast_mut::(value.as_mut()), #[cfg(not(feature = "no_object"))] Union::Map(value) => ::downcast_mut::(value.as_mut()), Union::Module(value) => ::downcast_mut::(value.as_mut()), Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::(), } } /// Cast the `Dynamic` as the system integer type `INT` and return it. /// Returns the name of the actual type if the cast fails. pub fn as_int(&self) -> Result { match self.0 { Union::Int(n) => Ok(n), _ => Err(self.type_name()), } } /// Cast the `Dynamic` as the system floating-point type `FLOAT` and return it. /// Returns the name of the actual type if the cast fails. #[cfg(not(feature = "no_float"))] pub fn as_float(&self) -> Result { match self.0 { Union::Float(n) => Ok(n), _ => Err(self.type_name()), } } /// Cast the `Dynamic` as a `bool` and return it. /// Returns the name of the actual type if the cast fails. pub fn as_bool(&self) -> Result { match self.0 { Union::Bool(b) => Ok(b), _ => Err(self.type_name()), } } /// Cast the `Dynamic` as a `char` and return it. /// Returns the name of the actual type if the cast fails. pub fn as_char(&self) -> Result { match self.0 { Union::Char(n) => Ok(n), _ => Err(self.type_name()), } } /// Cast the `Dynamic` as a string and return the string slice. /// Returns the name of the actual type if the cast fails. pub fn as_str(&self) -> Result<&str, &'static str> { match &self.0 { Union::Str(s) => Ok(s), _ => Err(self.type_name()), } } /// Convert the `Dynamic` into `String` and return it. /// Returns the name of the actual type if the cast fails. pub fn take_string(self) -> Result { self.take_immutable_string() .map(ImmutableString::into_owned) } /// Convert the `Dynamic` into `ImmutableString` and return it. /// Returns the name of the actual type if the cast fails. pub(crate) fn take_immutable_string(self) -> Result { match self.0 { Union::Str(s) => Ok(s), _ => Err(self.type_name()), } } } impl From<()> for Dynamic { fn from(value: ()) -> Self { Self(Union::Unit(value)) } } impl From for Dynamic { fn from(value: bool) -> Self { Self(Union::Bool(value)) } } impl From for Dynamic { fn from(value: INT) -> Self { Self(Union::Int(value)) } } #[cfg(not(feature = "no_float"))] impl From for Dynamic { fn from(value: FLOAT) -> Self { Self(Union::Float(value)) } } impl From for Dynamic { fn from(value: char) -> Self { Self(Union::Char(value)) } } impl> From for Dynamic { fn from(value: S) -> Self { Self(Union::Str(value.into())) } } #[cfg(not(feature = "no_index"))] impl From> for Dynamic { fn from(value: Vec) -> Self { Self(Union::Array(Box::new( value.into_iter().map(Dynamic::from).collect(), ))) } } #[cfg(not(feature = "no_index"))] impl From<&[T]> for Dynamic { fn from(value: &[T]) -> Self { Self(Union::Array(Box::new( value.iter().cloned().map(Dynamic::from).collect(), ))) } } #[cfg(not(feature = "no_object"))] impl, T: Variant + Clone> From> for Dynamic { fn from(value: HashMap) -> Self { Self(Union::Map(Box::new( value .into_iter() .map(|(k, v)| (k.into(), Dynamic::from(v))) .collect(), ))) } } /// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only /// be implemented by this crate. #[doc(hidden)] pub struct _Private;