450 lines
14 KiB
Rust
450 lines
14 KiB
Rust
use super::PackageStore;
|
|
|
|
use crate::any::{Dynamic, Variant};
|
|
use crate::calc_fn_hash;
|
|
use crate::engine::FnCallArgs;
|
|
use crate::result::EvalAltResult;
|
|
use crate::token::Position;
|
|
|
|
use crate::stdlib::{
|
|
any::TypeId,
|
|
boxed::Box,
|
|
iter::empty,
|
|
mem,
|
|
string::{String, ToString},
|
|
};
|
|
|
|
/// This macro makes it easy to define a _package_ and register functions into it.
|
|
///
|
|
/// Functions can be added to the package using a number of helper functions under the `packages` module,
|
|
/// such as `reg_unary`, `reg_binary_mut`, `reg_trinary_mut` etc.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::Dynamic;
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_binary;
|
|
///
|
|
/// fn add(x: i64, y: i64) -> i64 { x + y }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_binary(lib, "my_add", add, |v, _| Ok(v.into()));
|
|
/// // ^^^^^^^^^^^^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single function named 'my_add'.
|
|
#[macro_export]
|
|
macro_rules! def_package {
|
|
($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => {
|
|
#[doc=$comment]
|
|
pub struct $package($root::packages::PackageLibrary);
|
|
|
|
impl $root::packages::Package for $package {
|
|
fn new() -> Self {
|
|
let mut pkg = $root::packages::PackageStore::new();
|
|
Self::init(&mut pkg);
|
|
Self(pkg.into())
|
|
}
|
|
|
|
fn get(&self) -> $root::packages::PackageLibrary {
|
|
self.0.clone()
|
|
}
|
|
|
|
fn init($lib: &mut $root::packages::PackageStore) {
|
|
$block
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Check whether the correct number of arguments is passed to the function.
|
|
fn check_num_args(
|
|
name: &str,
|
|
num_args: usize,
|
|
args: &mut FnCallArgs,
|
|
pos: Position,
|
|
) -> Result<(), Box<EvalAltResult>> {
|
|
if args.len() != num_args {
|
|
Err(Box::new(EvalAltResult::ErrorFunctionArgsMismatch(
|
|
name.to_string(),
|
|
num_args,
|
|
args.len(),
|
|
pos,
|
|
)))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Add a function with no parameters to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::Dynamic;
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_none;
|
|
///
|
|
/// fn get_answer() -> i64 { 42 }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_none(lib, "my_answer", get_answer, |v, _| Ok(v.into()));
|
|
/// // ^^^^^^^^^^^^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single function named 'my_add_1'.
|
|
pub fn reg_none<R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn() -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
let hash = calc_fn_hash(empty(), fn_name, ([] as [TypeId; 0]).iter().cloned());
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 0, args, pos)?;
|
|
|
|
let r = func();
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with one parameter to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::Dynamic;
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_unary;
|
|
///
|
|
/// fn add_1(x: i64) -> i64 { x + 1 }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_unary(lib, "my_add_1", add_1, |v, _| Ok(v.into()));
|
|
/// // ^^^^^^^^^^^^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single function named 'my_add_1'.
|
|
pub fn reg_unary<T: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(T) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(T) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
|
|
|
|
let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::<T>()].iter().cloned());
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 1, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x = mem::take(*drain.next().unwrap()).cast::<T>();
|
|
|
|
let r = func(x);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with one mutable reference parameter to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::{Dynamic, EvalAltResult};
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_unary_mut;
|
|
///
|
|
/// fn inc(x: &mut i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
/// if *x == 0 {
|
|
/// return Err("boo! zero cannot be incremented!".into())
|
|
/// }
|
|
/// *x += 1;
|
|
/// Ok(().into())
|
|
/// }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_unary_mut(lib, "try_inc", inc, |r, _| r);
|
|
/// // ^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single fallible function named 'try_inc'
|
|
/// which takes a first argument of `&mut`, return a `Result<Dynamic, Box<EvalAltResult>>`.
|
|
pub fn reg_unary_mut<T: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut T) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(&mut T) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
|
|
|
|
let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::<T>()].iter().cloned());
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 1, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
|
|
|
let r = func(x);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with two parameters to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::Dynamic;
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_binary;
|
|
///
|
|
/// fn add(x: i64, y: i64) -> i64 { x + y }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_binary(lib, "my_add", add, |v, _| Ok(v.into()));
|
|
/// // ^^^^^^^^^^^^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single function named 'my_add'.
|
|
pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
|
|
|
let hash = calc_fn_hash(
|
|
empty(),
|
|
fn_name,
|
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
|
);
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 2, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x = mem::take(*drain.next().unwrap()).cast::<A>();
|
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
|
|
|
let r = func(x, y);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with two parameters (the first one being a mutable reference) to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use rhai::{Dynamic, EvalAltResult};
|
|
/// use rhai::def_package;
|
|
/// use rhai::packages::reg_binary_mut;
|
|
///
|
|
/// fn add(x: &mut i64, y: i64) -> Result<Dynamic, Box<EvalAltResult>> {
|
|
/// if y == 0 {
|
|
/// return Err("boo! cannot add zero!".into())
|
|
/// }
|
|
/// *x += y;
|
|
/// Ok(().into())
|
|
/// }
|
|
///
|
|
/// def_package!(rhai:MyPackage:"My super-duper package", lib,
|
|
/// {
|
|
/// reg_binary_mut(lib, "try_add", add, |r, _| r);
|
|
/// // ^^^^^^^^
|
|
/// // map into Result<Dynamic, Box<EvalAltResult>>
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// The above defines a package named 'MyPackage' with a single fallible function named 'try_add'
|
|
/// which takes a first argument of `&mut`, return a `Result<Dynamic, Box<EvalAltResult>>`.
|
|
pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
|
|
|
let hash = calc_fn_hash(
|
|
empty(),
|
|
fn_name,
|
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
|
);
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 2, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
|
|
|
let r = func(x, y);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with three parameters to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
|
|
|
let hash = calc_fn_hash(
|
|
empty(),
|
|
fn_name,
|
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
|
.iter()
|
|
.cloned(),
|
|
);
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 3, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x = mem::take(*drain.next().unwrap()).cast::<A>();
|
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
|
let z = mem::take(*drain.next().unwrap()).cast::<C>();
|
|
|
|
let r = func(x, y, z);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|
|
|
|
/// Add a function with three parameters (the first one is a mutable reference) to the package.
|
|
///
|
|
/// `map_result` is a function that maps the return type of the function to `Result<Dynamic, EvalAltResult>`.
|
|
pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R>(
|
|
lib: &mut PackageStore,
|
|
fn_name: &'static str,
|
|
|
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> R + 'static,
|
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> R + Send + Sync + 'static,
|
|
|
|
#[cfg(not(feature = "sync"))] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ 'static,
|
|
#[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result<Dynamic, Box<EvalAltResult>>
|
|
+ Send
|
|
+ Sync
|
|
+ 'static,
|
|
) {
|
|
//println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
|
|
|
let hash = calc_fn_hash(
|
|
empty(),
|
|
fn_name,
|
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
|
.iter()
|
|
.cloned(),
|
|
);
|
|
|
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
|
check_num_args(fn_name, 3, args, pos)?;
|
|
|
|
let mut drain = args.iter_mut();
|
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
|
let z = mem::take(*drain.next().unwrap()).cast::<C>();
|
|
|
|
let r = func(x, y, z);
|
|
map_result(r, pos)
|
|
});
|
|
|
|
lib.functions.insert(hash, f);
|
|
}
|