Merge pull request #12 from jhwgh1968/plugins
Basic Implementation of Plugins and Plugin Functions
This commit is contained in:
commit
c54cd43ffd
@ -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()
|
||||
|
@ -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<Dynamic, Box<EvalAltResult>>
|
||||
|
||||
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||
|
||||
#[cfg(feature = "sync")]
|
||||
pub type SharedPluginFunction = Arc<dyn PluginFunction + Send + Sync>;
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub type SharedPluginFunction = Rc<dyn PluginFunction>;
|
||||
|
||||
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<FnDef>),
|
||||
/// 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<FnAny>) -> 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))
|
||||
}
|
||||
}
|
||||
|
@ -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<PL: Plugin> {
|
||||
/// Register a custom function with the `Engine`.
|
||||
pub trait RegisterPlugin<PL: crate::plugin::Plugin> {
|
||||
/// 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<Dynamic, Box<EvalAltResult>> {
|
||||
/// let x1: &FLOAT = args[0].downcast_ref::<FLOAT>().unwrap();
|
||||
/// let y1: &FLOAT = args[1].downcast_ref::<FLOAT>().unwrap();
|
||||
/// let x2: &FLOAT = args[2].downcast_ref::<FLOAT>().unwrap();
|
||||
/// let y2: &FLOAT = args[3].downcast_ref::<FLOAT>().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<dyn PluginFunction> {
|
||||
/// 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<dyn Fn(INT) -> 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::<FLOAT>(),
|
||||
/// std::any::TypeId::of::<FLOAT>(),
|
||||
/// std::any::TypeId::of::<FLOAT>(),
|
||||
/// std::any::TypeId::of::<FLOAT>()],
|
||||
/// 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<rhai::EvalAltResult>> {
|
||||
///
|
||||
/// let mut engine = Engine::new();
|
||||
/// engine.register_plugin(AddOffsetPlugin(50));
|
||||
/// engine.register_plugin(AdvancedMathPlugin());
|
||||
///
|
||||
/// assert_eq!(engine.eval::<i64>("add_offset(42)")?, 92);
|
||||
/// assert_eq!(engine.eval::<FLOAT>(
|
||||
/// 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, ARGS> {
|
||||
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<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
mem::take(data).cast::<T>()
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugins")]
|
||||
impl<PL: Plugin> RegisterPlugin<PL> for Engine {
|
||||
fn register_plugin(&mut self, plugin: PL) {
|
||||
plugin.register_contents(self)
|
||||
plugin.register_contents(self);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
42
src/plugin.rs
Normal file
42
src/plugin.rs
Normal file
@ -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<Dynamic, Box<EvalAltResult>>;
|
||||
|
||||
fn clone_boxed(&self) -> Box<dyn PluginFunction>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user