1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Module which defines the function registration mechanism.

#![cfg(not(feature = "no_function"))]
#![allow(non_snake_case)]

use crate::types::dynamic::Variant;
use crate::{Engine, EvalAltResult, ParseError, Scope, SmartString, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

/// Trait to create a Rust closure from a script.
///
/// Not available under `no_function`.
pub trait Func<ARGS, RET> {
    type Output;

    /// Create a Rust closure from an [`AST`].
    ///
    /// The [`Engine`] and [`AST`] are consumed and basically embedded into the closure.
    ///
    /// # Example
    ///
    /// ```
    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
    /// use quad_compat_rhai::{Engine, Func};               // use 'Func' for 'create_from_ast'
    ///
    /// let engine = Engine::new();             // create a new 'Engine' just for this
    ///
    /// let ast = engine.compile("fn calc(x, y) { x + len(y) < 42 }")?;
    ///
    /// // Func takes two type parameters:
    /// //   1) a tuple made up of the types of the script function's parameters
    /// //   2) the return type of the script function
    ///
    /// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
    /// let func = Func::<(i64, &str), bool>::create_from_ast(
    /// //                ^^^^^^^^^^^ function parameter types in tuple
    ///
    ///                                             engine, // the 'Engine' is consumed into the closure
    ///                                             ast,    // the 'AST'
    ///                                             "calc"  // the entry-point function name
    ///                                         );
    ///
    /// func(123, "hello")? == false;           // call the anonymous function
    /// # Ok(())
    /// # }
    fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output;

    /// Create a Rust closure from a script.
    ///
    /// The [`Engine`] is consumed and basically embedded into the closure.
    ///
    /// # Example
    ///
    /// ```
    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
    /// use quad_compat_rhai::{Engine, Func};               // use 'Func' for 'create_from_script'
    ///
    /// let engine = Engine::new();             // create a new 'Engine' just for this
    ///
    /// let script = "fn calc(x, y) { x + len(y) < 42 }";
    ///
    /// // Func takes two type parameters:
    /// //   1) a tuple made up of the types of the script function's parameters
    /// //   2) the return type of the script function
    ///
    /// // 'func' will have type Box<dyn Fn(i64, String) -> Result<bool, Box<EvalAltResult>>> and is callable!
    /// let func = Func::<(i64, &str), bool>::create_from_script(
    /// //                ^^^^^^^^^^^ function parameter types in tuple
    ///
    ///                                             engine, // the 'Engine' is consumed into the closure
    ///                                             script, // the script, notice number of parameters must match
    ///                                             "calc"  // the entry-point function name
    ///                                         )?;
    ///
    /// func(123, "hello")? == false;           // call the anonymous function
    /// # Ok(())
    /// # }
    /// ```
    fn create_from_script(
        self,
        script: &str,
        entry_point: &str,
    ) -> Result<Self::Output, ParseError>;
}

macro_rules! def_anonymous_fn {
    () => {
        def_anonymous_fn!(imp);
    };
    (imp $($par:ident),*) => {
        impl<$($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine
        {
            #[cfg(feature = "sync")]
            type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>> + Send + Sync>;
            #[cfg(not(feature = "sync"))]
            type Output = Box<dyn Fn($($par),*) -> Result<RET, Box<EvalAltResult>>>;

            #[inline]
            fn create_from_ast(self, ast: AST, entry_point: &str) -> Self::Output {
                let fn_name: SmartString = entry_point.into();
                Box::new(move |$($par),*| self.call_fn(&mut Scope::new(), &ast, &fn_name, ($($par,)*)))
            }

            #[inline]
            fn create_from_script(self, script: &str, entry_point: &str) -> Result<Self::Output, ParseError> {
                let ast = self.compile(script)?;
                Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point))
            }
        }
    };
    ($p0:ident $(, $p:ident)*) => {
        def_anonymous_fn!(imp $p0 $(, $p)*);
        def_anonymous_fn!($($p),*);
    };
}

def_anonymous_fn!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V);