diff --git a/src/engine.rs b/src/engine.rs index 453ee184..d578aaf8 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1652,6 +1652,7 @@ impl Engine { let mut scope = Scope::new(); self.call_script_fn(&mut scope, state, lib, name, fn_def, args, *pos, level) } + Ok(x) if x.is_plugin_fn() => x.get_plugin_fn().call(args.as_mut(), *pos), Ok(x) => x.get_native_fn()(args.as_mut()).map_err(|err| err.new_position(*pos)), Err(err) if def_val.is_some() diff --git a/src/fn_native.rs b/src/fn_native.rs index 6c43adc6..35ddcfdf 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,7 @@ use crate::any::Dynamic; use crate::parser::FnDef; use crate::result::EvalAltResult; +use crate::token::Position; use crate::stdlib::{boxed::Box, rc::Rc, sync::Arc}; @@ -57,6 +58,13 @@ pub type FnAny = dyn Fn(&mut FnCallArgs) -> Result> pub type IteratorFn = fn(Dynamic) -> Box>; +#[cfg(feature = "sync")] +pub type SharedPluginFunction = Arc; +#[cfg(not(feature = "sync"))] +pub type SharedPluginFunction = Rc; + +use crate::plugin::PluginFunction; + /// A type encapsulating a function callable by Rhai. #[derive(Clone)] pub enum CallableFunction { @@ -69,6 +77,8 @@ pub enum CallableFunction { Iterator(IteratorFn), /// A script-defined function. Script(Shared), + /// A plugin-defined function, + Plugin(SharedPluginFunction), } impl CallableFunction { @@ -77,6 +87,7 @@ impl CallableFunction { match self { Self::Pure(_) => true, Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false, + Self::Plugin(_) => false, } } /// Is this a pure native Rust method-call? @@ -84,6 +95,7 @@ impl CallableFunction { match self { Self::Method(_) => true, Self::Pure(_) | Self::Iterator(_) | Self::Script(_) => false, + Self::Plugin(_) => false, } } /// Is this an iterator function? @@ -91,6 +103,7 @@ impl CallableFunction { match self { Self::Iterator(_) => true, Self::Pure(_) | Self::Method(_) | Self::Script(_) => false, + Self::Plugin(_) => false, } } /// Is this a Rhai-scripted function? @@ -98,6 +111,14 @@ impl CallableFunction { match self { Self::Script(_) => true, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, + Self::Plugin(_) => false, + } + } + /// Is this a plugin-defined function? + pub fn is_plugin_fn(&self) -> bool { + match self { + Self::Plugin(_) => true, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Script(_) => false, } } /// Get a reference to a native Rust function. @@ -109,6 +130,7 @@ impl CallableFunction { match self { Self::Pure(f) | Self::Method(f) => f.as_ref(), Self::Iterator(_) | Self::Script(_) => panic!(), + Self::Plugin(_) => panic!(), } } /// Get a reference to a script-defined function definition. @@ -120,6 +142,7 @@ impl CallableFunction { match self { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => panic!(), Self::Script(f) => f, + Self::Plugin(_) => panic!(), } } /// Get a reference to an iterator function. @@ -131,6 +154,18 @@ impl CallableFunction { match self { Self::Iterator(f) => *f, Self::Pure(_) | Self::Method(_) | Self::Script(_) => panic!(), + Self::Plugin(_) => panic!(), + } + } + /// Get a reference to a plugin function. + /// + /// # Panics + /// + /// Panics if the `CallableFunction` is not `Plugin`. + pub fn get_plugin_fn<'s>(&'s self) -> SharedPluginFunction { + match self { + Self::Plugin(f) => f.clone(), + Self::Pure(_) | Self::Method(_) | Self::Script(_) | Self::Iterator(_) => panic!(), } } /// Create a new `CallableFunction::Pure`. @@ -141,4 +176,16 @@ impl CallableFunction { pub fn from_method(func: Box) -> Self { Self::Method(func.into()) } + + #[cfg(feature = "sync")] + /// Create a new `CallableFunction::Plugin`. + pub fn from_plugin(plugin: impl PluginFunction + 'static + Send + Sync) -> Self { + Self::Plugin(Arc::new(plugin)) + } + + #[cfg(not(feature = "sync"))] + /// Create a new `CallableFunction::Plugin`. + pub fn from_plugin(plugin: impl PluginFunction + 'static) -> Self { + Self::Plugin(Rc::new(plugin)) + } } diff --git a/src/fn_register.rs b/src/fn_register.rs index f3bfc17c..4d962cf9 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -6,6 +6,7 @@ use crate::any::{Dynamic, Variant}; use crate::engine::Engine; use crate::fn_native::{CallableFunction, FnAny, FnCallArgs}; use crate::parser::FnAccess; +use crate::plugin::Plugin; use crate::result::EvalAltResult; use crate::stdlib::{any::TypeId, boxed::Box, mem}; @@ -13,40 +14,76 @@ use crate::stdlib::{any::TypeId, boxed::Box, mem}; /// A trait to register custom plugins with the `Engine`. /// /// A plugin consists of a number of functions. All functions will be registered with the engine. -#[cfg(feature = "plugins")] -pub trait RegisterPlugin { - /// Register a custom function with the `Engine`. +pub trait RegisterPlugin { + /// Allow extensions of the engine's behavior. + /// + /// This can include importing modules, registering functions to the global name space, and + /// more. /// /// # Example /// /// ``` - /// use rhai::{Dynamic, Engine, INT, Plugin, RegisterDynamicFn, RegisterPlugin}; + /// use rhai::{FLOAT, INT, Module, ModuleResolver, RegisterFn, RegisterPlugin}; + /// use rhai::plugin::*; + /// use rhai::module_resolvers::*; /// - /// // A simple custom plugin type. This should not usually be done with hand-written code. - /// struct AddOffsetPlugin(INT); - /// impl AddOffsetPlugin { - /// fn add_offset(&self, x: INT) -> Dynamic { - /// Dynamic::from(x + self.0) + /// // A function we want to expose to Rhai. + /// #[derive(Copy, Clone)] + /// struct DistanceFunction(); + /// + /// impl PluginFunction for DistanceFunction { + /// fn is_method_call(&self) -> bool { false } + /// fn is_varadic(&self) -> bool { false } + /// + /// fn call(&self, args: &[&mut Dynamic], pos: Position) -> Result> { + /// let x1: &FLOAT = args[0].downcast_ref::().unwrap(); + /// let y1: &FLOAT = args[1].downcast_ref::().unwrap(); + /// let x2: &FLOAT = args[2].downcast_ref::().unwrap(); + /// let y2: &FLOAT = args[3].downcast_ref::().unwrap(); + /// let square_sum = (y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0); + /// Ok(Dynamic::from(square_sum.sqrt())) + /// } + /// + /// fn clone_boxed(&self) -> Box { + /// Box::new(DistanceFunction()) /// } /// } - /// impl Plugin for AddOffsetPlugin { - /// fn name(&self) -> &str { - /// "My Plugin" - /// } + /// + /// // A simple custom plugin. This should not usually be done with hand-written code. + /// #[derive(Copy, Clone)] + /// pub struct AdvancedMathPlugin(); + /// + /// impl Plugin for AdvancedMathPlugin { /// fn register_contents(self, engine: &mut Engine) { - /// let add_offset_fn: Box Dynamic> = { - /// Box::new(move |x| self.add_offset(x)) - /// }; - /// engine.register_dynamic_fn("add_offset", add_offset_fn); + /// // Plugins are allowed to have side-effects on the engine. + /// engine.register_fn("get_mystic_number", || { 42 as FLOAT }); + /// + /// // Main purpose: create a module to expose the functions to Rhai. + /// // + /// // This is currently a hack. There needs to be a better API here for "plugin" + /// // modules. + /// let mut m = Module::new(); + /// m.set_fn("euclidean_distance".to_string(), FnAccess::Public, + /// &[std::any::TypeId::of::(), + /// std::any::TypeId::of::(), + /// std::any::TypeId::of::(), + /// std::any::TypeId::of::()], + /// CallableFunction::from_plugin(DistanceFunction())); + /// let mut r = StaticModuleResolver::new(); + /// r.insert("Math::Advanced".to_string(), m); + /// engine.set_module_resolver(Some(r)); /// } /// } /// + /// /// # fn main() -> Result<(), Box> { /// /// let mut engine = Engine::new(); - /// engine.register_plugin(AddOffsetPlugin(50)); + /// engine.register_plugin(AdvancedMathPlugin()); /// - /// assert_eq!(engine.eval::("add_offset(42)")?, 92); + /// assert_eq!(engine.eval::( + /// r#"import "Math::Advanced" as math; + /// let x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#)?, 41.0); /// # Ok(()) /// # } /// ``` @@ -115,15 +152,6 @@ pub trait RegisterResultFn { fn register_result_fn(&mut self, name: &str, f: FN); } -/// Represents an externally-written plugin for the Rhai interpreter. -/// -/// This should not be used directly. Use the `plugin_module!` macro instead. -#[cfg(feature = "plugins")] -pub trait Plugin { - fn register_contents(self, engine: &mut Engine); - fn name(&self) -> &str; -} - // These types are used to build a unique _marker_ tuple type for each combination // of function parameter types in order to make each trait implementation unique. // That is because stable Rust currently does not allow distinguishing implementations @@ -156,10 +184,9 @@ pub fn by_value(data: &mut Dynamic) -> T { mem::take(data).cast::() } -#[cfg(feature = "plugins")] impl RegisterPlugin for Engine { fn register_plugin(&mut self, plugin: PL) { - plugin.register_contents(self) + plugin.register_contents(self); } } diff --git a/src/lib.rs b/src/lib.rs index 39920c0f..e0f2245c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,10 +75,14 @@ mod engine; mod error; mod fn_call; mod fn_func; -mod fn_native; +pub mod fn_native; mod fn_register; mod module; mod optimize; +#[cfg(not(feature = "no_module"))] +pub mod plugin; +#[cfg(feature = "no_module")] +mod plugin; pub mod packages; mod parser; mod result; @@ -91,9 +95,8 @@ mod utils; pub use any::Dynamic; pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; -#[cfg(feature = "plugins")] -pub use fn_register::{Plugin, RegisterPlugin}; pub use fn_register::{RegisterFn, RegisterResultFn}; +pub use fn_register::RegisterPlugin; pub use module::Module; pub use parser::{ImmutableString, AST, INT}; pub use result::EvalAltResult; diff --git a/src/plugin.rs b/src/plugin.rs new file mode 100644 index 00000000..8159d3c8 --- /dev/null +++ b/src/plugin.rs @@ -0,0 +1,42 @@ +//! Module defining plugins in Rhai. Is exported for use by plugin authors. + +pub use crate::Engine; +pub use crate::any::{Dynamic, Variant}; +pub use crate::fn_native::{CallableFunction, FnCallArgs, IteratorFn}; +pub use crate::parser::{ + FnAccess, + FnAccess::{Private, Public}, + AST, +}; +pub use crate::result::EvalAltResult; +pub use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; +pub use crate::token::{Position, Token}; +pub use crate::utils::StaticVec; + +#[cfg(features = "sync")] +/// Represents an externally-written plugin for the Rhai interpreter. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait Plugin: Send { + fn register_contents(self, engine: &mut Engine); +} + +#[cfg(not(features = "sync"))] +/// Represents an externally-written plugin for the Rhai interpreter. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait Plugin: Send + Sync { + fn register_contents(self, engine: &mut Engine); +} + +/// Represents a function that is statically defined within a plugin. +/// +/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead. +pub trait PluginFunction { + fn is_method_call(&self) -> bool; + fn is_varadic(&self) -> bool; + + fn call(&self, args: &[&mut Dynamic], pos: Position) -> Result>; + + fn clone_boxed(&self) -> Box; +}