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> /// }); /// ``` /// /// 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> { 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`. /// /// # 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> /// }); /// ``` /// /// The above defines a package named 'MyPackage' with a single function named 'my_add_1'. pub fn reg_none( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + 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`. /// /// # 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> /// }); /// ``` /// /// The above defines a package named 'MyPackage' with a single function named 'my_add_1'. pub fn reg_unary( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}({})", fn_name, crate::std::any::type_name::()); let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::()].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::(); 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`. /// /// # Examples /// /// ``` /// use rhai::{Dynamic, EvalAltResult}; /// use rhai::def_package; /// use rhai::packages::reg_unary_mut; /// /// fn inc(x: &mut i64) -> Result> { /// 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> /// }); /// ``` /// /// 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>`. pub fn reg_unary_mut( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}(&mut {})", fn_name, crate::std::any::type_name::()); let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::()].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`. /// /// # 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> /// }); /// ``` /// /// The above defines a package named 'MyPackage' with a single function named 'my_add'. pub fn reg_binary( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}({}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::()); let hash = calc_fn_hash( empty(), fn_name, [TypeId::of::(), TypeId::of::()].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::(); let y = mem::take(*drain.next().unwrap()).cast::(); 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`. /// /// # Examples /// /// ``` /// use rhai::{Dynamic, EvalAltResult}; /// use rhai::def_package; /// use rhai::packages::reg_binary_mut; /// /// fn add(x: &mut i64, y: i64) -> Result> { /// 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> /// }); /// ``` /// /// 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>`. pub fn reg_binary_mut( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::()); let hash = calc_fn_hash( empty(), fn_name, [TypeId::of::(), TypeId::of::()].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::(); 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`. pub fn reg_trinary( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::(), crate::std::any::type_name::()); let hash = calc_fn_hash( empty(), fn_name, [TypeId::of::(), TypeId::of::(), TypeId::of::()] .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::(); let y = mem::take(*drain.next().unwrap()).cast::(); let z = mem::take(*drain.next().unwrap()).cast::(); 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`. pub fn reg_trinary_mut( 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> + 'static, #[cfg(feature = "sync")] map_result: impl Fn(R, Position) -> Result> + Send + Sync + 'static, ) { //println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::(), crate::std::any::type_name::(), crate::std::any::type_name::()); let hash = calc_fn_hash( empty(), fn_name, [TypeId::of::(), TypeId::of::(), TypeId::of::()] .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::(); let z = mem::take(*drain.next().unwrap()).cast::(); let r = func(x, y, z); map_result(r, pos) }); lib.functions.insert(hash, f); }