//! Module defining interfaces to native-Rust functions. use crate::ast::{FnAccess, FnCallHashes}; use crate::engine::Imports; use crate::fn_call::FnCallArgs; use crate::plugin::PluginFunction; use crate::{ calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Module, Position, RhaiResult, }; use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] pub trait SendSync: Send + Sync {} /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(feature = "sync")] impl SendSync for T {} /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(not(feature = "sync"))] pub trait SendSync {} /// Trait that maps to `Send + Sync` only under the `sync` feature. #[cfg(not(feature = "sync"))] impl SendSync for T {} /// Immutable reference-counted container. #[cfg(not(feature = "sync"))] pub use std::rc::Rc as Shared; /// Immutable reference-counted container. #[cfg(feature = "sync")] pub use std::sync::Arc as Shared; /// Synchronized shared object. /// /// Not available under `no_closure`. #[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "sync"))] pub use std::cell::RefCell as Locked; /// Synchronized shared object. /// /// Not available under `no_closure`. #[cfg(not(feature = "no_closure"))] #[cfg(feature = "sync")] pub use std::sync::RwLock as Locked; /// Context of a native Rust function call. #[derive(Debug)] pub struct NativeCallContext<'a> { engine: &'a Engine, fn_name: &'a str, source: Option<&'a str>, mods: Option<&'a Imports>, lib: &'a [&'a Module], } impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)> for NativeCallContext<'a> { #[inline(always)] fn from(value: (&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)) -> Self { Self { engine: value.0, fn_name: value.1, source: value.2, mods: Some(value.3), lib: value.4.as_ref(), } } } impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)> for NativeCallContext<'a> { #[inline(always)] fn from(value: (&'a Engine, &'a str, &'a M)) -> Self { Self { engine: value.0, fn_name: value.1, source: None, mods: None, lib: value.2.as_ref(), } } } impl<'a> NativeCallContext<'a> { /// Create a new [`NativeCallContext`]. #[inline(always)] #[must_use] pub const fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { Self { engine, fn_name, source: None, mods: None, lib, } } /// _(INTERNALS)_ Create a new [`NativeCallContext`]. /// Exported under the `internals` feature only. /// /// Not available under `no_module`. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub const fn new_with_all_fields( engine: &'a Engine, fn_name: &'a str, source: &'a Option<&str>, imports: &'a Imports, lib: &'a [&Module], ) -> Self { Self { engine, fn_name, source: source.clone(), mods: Some(imports), lib, } } /// The current [`Engine`]. #[inline(always)] #[must_use] pub const fn engine(&self) -> &Engine { self.engine } /// Name of the function called. #[inline(always)] #[must_use] pub const fn fn_name(&self) -> &str { self.fn_name } /// The current source. #[inline(always)] #[must_use] pub const fn source(&self) -> Option<&str> { self.source } /// Get an iterator over the current set of modules imported via `import` statements. /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub fn iter_imports(&self) -> impl Iterator { self.mods.iter().flat_map(|&m| m.iter()) } /// Get an iterator over the current set of modules imported via `import` statements. #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline(always)] #[must_use] pub(crate) fn iter_imports_raw( &self, ) -> impl Iterator)> { self.mods.iter().flat_map(|&m| m.iter_raw()) } /// _(INTERNALS)_ The current set of modules imported via `import` statements. /// Exported under the `internals` feature only. /// /// Not available under `no_module`. #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub const fn imports(&self) -> Option<&Imports> { self.mods } /// Get an iterator over the namespaces containing definitions of all script-defined functions. #[inline(always)] #[must_use] pub fn iter_namespaces(&self) -> impl Iterator { self.lib.iter().cloned() } /// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] #[inline(always)] #[must_use] pub const fn namespaces(&self) -> &[&Module] { self.lib } /// Call a function inside the call context. /// /// # WARNING /// /// All arguments may be _consumed_, meaning that they may be replaced by `()`. /// This is to avoid unnecessarily cloning the arguments. /// /// Do not use the arguments after this call. If they are needed afterwards, /// clone them _before_ calling this function. /// /// If `is_method` is [`true`], the first argument is assumed to be passed /// by reference and is not consumed. #[inline(always)] #[must_use] pub fn call_fn_dynamic_raw( &self, fn_name: impl AsRef, is_method_call: bool, args: &mut [&mut Dynamic], ) -> RhaiResult { let fn_name = fn_name.as_ref(); let hash = if is_method_call { FnCallHashes::from_script_and_native( calc_fn_hash(fn_name, args.len() - 1), calc_fn_hash(fn_name, args.len()), ) } else { FnCallHashes::from_script(calc_fn_hash(fn_name, args.len())) }; self.engine() .exec_fn_call( &mut self.mods.cloned().unwrap_or_default(), &mut Default::default(), self.lib, fn_name, hash, args, is_method_call, is_method_call, Position::NONE, None, 0, ) .map(|(r, _)| r) } } /// Consume a [`Shared`] resource and return a mutable reference to the wrapped value. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. #[inline(always)] #[must_use] pub fn shared_make_mut(value: &mut Shared) -> &mut T { Shared::make_mut(value) } /// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise. #[inline(always)] #[must_use] pub fn shared_take_or_clone(value: Shared) -> T { shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone()) } /// Consume a [`Shared`] resource if is unique (i.e. not shared). #[inline(always)] #[must_use] pub fn shared_try_take(value: Shared) -> Result> { Shared::try_unwrap(value) } /// Consume a [`Shared`] resource, assuming that it is unique (i.e. not shared). /// /// # Panics /// /// Panics if the resource is shared (i.e. has other outstanding references). #[inline(always)] #[must_use] pub fn shared_take(value: Shared) -> T { shared_try_take(value) .ok() .expect("resource should have no outstanding references") } /// A general function trail object. #[cfg(not(feature = "sync"))] pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult; /// A general function trail object. #[cfg(feature = "sync")] pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult + Send + Sync; /// A standard function that gets an iterator from a type. pub type IteratorFn = fn(Dynamic) -> Box>; #[cfg(not(feature = "sync"))] pub type FnPlugin = dyn PluginFunction; #[cfg(feature = "sync")] pub type FnPlugin = dyn PluginFunction + Send + Sync; /// A standard callback function for progress reporting. #[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "sync"))] pub type OnProgressCallback = Box Option + 'static>; /// A standard callback function for progress reporting. #[cfg(not(feature = "unchecked"))] #[cfg(feature = "sync")] pub type OnProgressCallback = Box Option + Send + Sync + 'static>; /// A standard callback function for printing. #[cfg(not(feature = "sync"))] pub type OnPrintCallback = Box; /// A standard callback function for printing. #[cfg(feature = "sync")] pub type OnPrintCallback = Box; /// A standard callback function for debugging. #[cfg(not(feature = "sync"))] pub type OnDebugCallback = Box, Position) + 'static>; /// A standard callback function for debugging. #[cfg(feature = "sync")] pub type OnDebugCallback = Box, Position) + Send + Sync + 'static>; /// A standard callback function for variable access. #[cfg(not(feature = "sync"))] pub type OnVarCallback = Box Result, Box> + 'static>; /// A standard callback function for variable access. #[cfg(feature = "sync")] pub type OnVarCallback = Box< dyn Fn(&str, usize, &EvalContext) -> Result, Box> + Send + Sync + 'static, >; /// A type encapsulating a function callable by Rhai. #[derive(Clone)] pub enum CallableFunction { /// A pure native Rust function with all arguments passed by value. Pure(Shared), /// A native Rust object method with the first argument passed by reference, /// and the rest passed by value. Method(Shared), /// An iterator function. Iterator(IteratorFn), /// A plugin function, Plugin(Shared), /// A script-defined function. /// /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] Script(Shared), } impl fmt::Debug for CallableFunction { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pure(_) => write!(f, "NativePureFunction"), Self::Method(_) => write!(f, "NativeMethod"), Self::Iterator(_) => write!(f, "NativeIterator"), Self::Plugin(_) => write!(f, "PluginFunction"), #[cfg(not(feature = "no_function"))] Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), } } } impl fmt::Display for CallableFunction { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pure(_) => write!(f, "NativePureFunction"), Self::Method(_) => write!(f, "NativeMethod"), Self::Iterator(_) => write!(f, "NativeIterator"), Self::Plugin(_) => write!(f, "PluginFunction"), #[cfg(not(feature = "no_function"))] CallableFunction::Script(s) => fmt::Display::fmt(s, f), } } } impl CallableFunction { /// Is this a pure native Rust function? #[inline(always)] #[must_use] pub fn is_pure(&self) -> bool { match self { Self::Pure(_) => true, Self::Method(_) | Self::Iterator(_) => false, Self::Plugin(p) => !p.is_method_call(), #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } /// Is this a native Rust method function? #[inline(always)] #[must_use] pub fn is_method(&self) -> bool { match self { Self::Method(_) => true, Self::Pure(_) | Self::Iterator(_) => false, Self::Plugin(p) => p.is_method_call(), #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } /// Is this an iterator function? #[inline(always)] #[must_use] pub fn is_iter(&self) -> bool { match self { Self::Iterator(_) => true, Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false, #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } /// Is this a Rhai-scripted function? #[inline(always)] #[must_use] pub fn is_script(&self) -> bool { match self { #[cfg(not(feature = "no_function"))] Self::Script(_) => true, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, } } /// Is this a plugin function? #[inline(always)] #[must_use] pub fn is_plugin_fn(&self) -> bool { match self { Self::Plugin(_) => true, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } /// Is this a native Rust function? #[inline(always)] #[must_use] pub fn is_native(&self) -> bool { match self { Self::Pure(_) | Self::Method(_) => true, Self::Plugin(_) => true, Self::Iterator(_) => true, #[cfg(not(feature = "no_function"))] Self::Script(_) => false, } } /// Get the access mode. #[inline(always)] #[must_use] pub fn access(&self) -> FnAccess { match self { Self::Plugin(_) => FnAccess::Public, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, #[cfg(not(feature = "no_function"))] Self::Script(f) => f.access, } } /// Get a shared reference to a native Rust function. #[inline(always)] #[must_use] pub fn get_native_fn(&self) -> Option<&Shared> { match self { Self::Pure(f) | Self::Method(f) => Some(f), Self::Iterator(_) | Self::Plugin(_) => None, #[cfg(not(feature = "no_function"))] Self::Script(_) => None, } } /// Get a shared reference to a script-defined function definition. /// /// Not available under `no_function`. #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] pub const fn get_script_fn_def(&self) -> Option<&Shared> { match self { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None, Self::Script(f) => Some(f), } } /// Get a reference to an iterator function. #[inline(always)] #[must_use] pub fn get_iter_fn(&self) -> Option { match self { Self::Iterator(f) => Some(*f), Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None, #[cfg(not(feature = "no_function"))] Self::Script(_) => None, } } /// Get a shared reference to a plugin function. #[inline(always)] #[must_use] pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared> { match self { Self::Plugin(f) => Some(f), Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, #[cfg(not(feature = "no_function"))] Self::Script(_) => None, } } /// Create a new [`CallableFunction::Pure`]. #[inline(always)] #[must_use] pub fn from_pure(func: Box) -> Self { Self::Pure(func.into()) } /// Create a new [`CallableFunction::Method`]. #[inline(always)] #[must_use] pub fn from_method(func: Box) -> Self { Self::Method(func.into()) } /// Create a new [`CallableFunction::Plugin`]. #[inline(always)] #[must_use] pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { Self::Plugin((Box::new(func) as Box).into()) } } impl From for CallableFunction { #[inline(always)] fn from(func: IteratorFn) -> Self { Self::Iterator(func) } } #[cfg(not(feature = "no_function"))] impl From for CallableFunction { #[inline(always)] fn from(_func: crate::ast::ScriptFnDef) -> Self { Self::Script(_func.into()) } } #[cfg(not(feature = "no_function"))] impl From> for CallableFunction { #[inline(always)] fn from(_func: Shared) -> Self { Self::Script(_func) } } impl From for CallableFunction { #[inline(always)] fn from(func: T) -> Self { Self::from_plugin(func) } } impl From> for CallableFunction { #[inline(always)] fn from(func: Shared) -> Self { Self::Plugin(func.into()) } }