2020-03-08 12:54:02 +01:00
|
|
|
//! Module that defines the extern API of `Engine`.
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
use crate::any::{Any, AnyExt, Dynamic};
|
|
|
|
use crate::call::FuncArgs;
|
2020-03-10 07:09:05 +01:00
|
|
|
use crate::engine::{Engine, FnAny, FnCallArgs, FnIntExt, FnSpec};
|
2020-03-04 15:00:01 +01:00
|
|
|
use crate::error::ParseError;
|
|
|
|
use crate::fn_register::RegisterFn;
|
|
|
|
use crate::parser::{lex, parse, Position, AST};
|
|
|
|
use crate::result::EvalAltResult;
|
2020-03-03 08:20:20 +01:00
|
|
|
use crate::scope::Scope;
|
2020-03-10 03:07:44 +01:00
|
|
|
use std::{
|
|
|
|
any::{type_name, TypeId},
|
|
|
|
fs::File,
|
|
|
|
io::prelude::*,
|
|
|
|
sync::Arc,
|
|
|
|
};
|
2020-03-02 07:28:42 +01:00
|
|
|
|
2020-03-06 17:29:45 +01:00
|
|
|
impl<'e> Engine<'e> {
|
2020-03-04 15:00:01 +01:00
|
|
|
pub(crate) fn register_fn_raw(
|
|
|
|
&mut self,
|
|
|
|
fn_name: &str,
|
|
|
|
args: Option<Vec<TypeId>>,
|
|
|
|
f: Box<FnAny>,
|
|
|
|
) {
|
|
|
|
debug_println!(
|
2020-03-08 12:54:02 +01:00
|
|
|
"Register function: {} with {}",
|
2020-03-04 15:00:01 +01:00
|
|
|
fn_name,
|
2020-03-06 08:49:38 +01:00
|
|
|
if let Some(a) = &args {
|
2020-03-08 12:54:02 +01:00
|
|
|
format!(
|
|
|
|
"{} parameter{}",
|
|
|
|
a.len(),
|
|
|
|
if a.len() > 1 { "s" } else { "" }
|
|
|
|
)
|
2020-03-06 08:49:38 +01:00
|
|
|
} else {
|
2020-03-08 12:54:02 +01:00
|
|
|
"no parameter".to_string()
|
2020-03-06 08:49:38 +01:00
|
|
|
}
|
2020-03-04 15:00:01 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
let spec = FnSpec {
|
|
|
|
name: fn_name.to_string().into(),
|
|
|
|
args,
|
|
|
|
};
|
|
|
|
|
2020-03-08 12:54:02 +01:00
|
|
|
self.ext_functions.insert(spec, Arc::new(FnIntExt::Ext(f)));
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a custom type for use with the `Engine`.
|
|
|
|
/// The type must be `Clone`.
|
|
|
|
pub fn register_type<T: Any + Clone>(&mut self) {
|
2020-03-08 12:54:02 +01:00
|
|
|
self.register_type_with_name::<T>(type_name::<T>());
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a custom type for use with the `Engine` with a name for the `type_of` function.
|
|
|
|
/// The type must be `Clone`.
|
2020-03-08 12:54:02 +01:00
|
|
|
pub fn register_type_with_name<T: Any + Clone>(&mut self, name: &str) {
|
2020-03-04 15:00:01 +01:00
|
|
|
// Add the pretty-print type name into the map
|
2020-03-08 12:54:02 +01:00
|
|
|
self.type_names
|
|
|
|
.insert(type_name::<T>().to_string(), name.to_string());
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register an iterator adapter for a type with the `Engine`.
|
|
|
|
pub fn register_iterator<T: Any, F>(&mut self, f: F)
|
|
|
|
where
|
|
|
|
F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static,
|
|
|
|
{
|
|
|
|
self.type_iterators.insert(TypeId::of::<T>(), Arc::new(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a getter function for a member of a registered type with the `Engine`.
|
|
|
|
pub fn register_get<T: Any + Clone, U: Any + Clone>(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
2020-03-04 16:06:05 +01:00
|
|
|
callback: impl Fn(&mut T) -> U + 'static,
|
2020-03-04 15:00:01 +01:00
|
|
|
) {
|
|
|
|
let get_name = "get$".to_string() + name;
|
2020-03-04 16:06:05 +01:00
|
|
|
self.register_fn(&get_name, callback);
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a setter function for a member of a registered type with the `Engine`.
|
|
|
|
pub fn register_set<T: Any + Clone, U: Any + Clone>(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
2020-03-04 16:06:05 +01:00
|
|
|
callback: impl Fn(&mut T, U) -> () + 'static,
|
2020-03-04 15:00:01 +01:00
|
|
|
) {
|
|
|
|
let set_name = "set$".to_string() + name;
|
2020-03-04 16:06:05 +01:00
|
|
|
self.register_fn(&set_name, callback);
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Shorthand for registering both getter and setter functions
|
|
|
|
/// of a registered type with the `Engine`.
|
|
|
|
pub fn register_get_set<T: Any + Clone, U: Any + Clone>(
|
|
|
|
&mut self,
|
|
|
|
name: &str,
|
|
|
|
get_fn: impl Fn(&mut T) -> U + 'static,
|
|
|
|
set_fn: impl Fn(&mut T, U) -> () + 'static,
|
|
|
|
) {
|
|
|
|
self.register_get(name, get_fn);
|
|
|
|
self.register_set(name, set_fn);
|
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Compile a string into an AST.
|
2020-03-09 14:57:07 +01:00
|
|
|
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
|
2020-03-02 07:28:42 +01:00
|
|
|
let tokens = lex(input);
|
2020-03-09 14:57:07 +01:00
|
|
|
parse(&mut tokens.peekable(), self.optimize)
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
fn read_file(filename: &str) -> Result<String, EvalAltResult> {
|
2020-03-02 07:28:42 +01:00
|
|
|
let mut f = File::open(filename)
|
2020-03-04 15:00:01 +01:00
|
|
|
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
|
2020-03-02 07:28:42 +01:00
|
|
|
|
|
|
|
let mut contents = String::new();
|
|
|
|
|
|
|
|
f.read_to_string(&mut contents)
|
2020-03-04 15:00:01 +01:00
|
|
|
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
|
2020-03-10 10:10:33 +01:00
|
|
|
.map(|_| contents)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compile a file into an AST.
|
|
|
|
pub fn compile_file(&self, filename: &str) -> Result<AST, EvalAltResult> {
|
|
|
|
Self::read_file(filename)
|
|
|
|
.and_then(|contents| self.compile(&contents).map_err(|err| err.into()))
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate a file.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
2020-03-10 10:10:33 +01:00
|
|
|
Self::read_file(filename).and_then(|contents| self.eval::<T>(&contents))
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate a string.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
|
|
|
let mut scope = Scope::new();
|
2020-03-10 02:30:12 +01:00
|
|
|
self.eval_with_scope(&mut scope, false, input)
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate a string with own scope.
|
|
|
|
///
|
|
|
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
|
|
|
/// and not cleared from run to run.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn eval_with_scope<T: Any + Clone>(
|
|
|
|
&mut self,
|
|
|
|
scope: &mut Scope,
|
2020-03-10 02:30:12 +01:00
|
|
|
retain_functions: bool,
|
2020-03-02 07:28:42 +01:00
|
|
|
input: &str,
|
|
|
|
) -> Result<T, EvalAltResult> {
|
2020-03-09 14:57:07 +01:00
|
|
|
let ast = self.compile(input).map_err(EvalAltResult::ErrorParsing)?;
|
2020-03-10 02:30:12 +01:00
|
|
|
self.eval_ast_with_scope(scope, retain_functions, &ast)
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate an AST.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
|
|
|
let mut scope = Scope::new();
|
2020-03-10 02:30:12 +01:00
|
|
|
self.eval_ast_with_scope(&mut scope, false, ast)
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate an AST with own scope.
|
|
|
|
///
|
|
|
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
|
|
|
/// and not cleared from run to run.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn eval_ast_with_scope<T: Any + Clone>(
|
|
|
|
&mut self,
|
|
|
|
scope: &mut Scope,
|
2020-03-10 02:30:12 +01:00
|
|
|
retain_functions: bool,
|
2020-03-02 07:28:42 +01:00
|
|
|
ast: &AST,
|
|
|
|
) -> Result<T, EvalAltResult> {
|
2020-03-10 10:10:33 +01:00
|
|
|
fn eval_ast_internal(
|
|
|
|
engine: &mut Engine,
|
|
|
|
scope: &mut Scope,
|
|
|
|
retain_functions: bool,
|
|
|
|
ast: &AST,
|
|
|
|
) -> Result<Dynamic, EvalAltResult> {
|
2020-03-11 06:28:12 +01:00
|
|
|
#[cfg(feature = "no_function")]
|
|
|
|
let AST(statements) = ast;
|
2020-03-10 10:10:33 +01:00
|
|
|
|
2020-03-11 06:28:12 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
let statements = {
|
|
|
|
let AST(statements, functions) = ast;
|
|
|
|
|
|
|
|
functions.iter().for_each(|f| {
|
|
|
|
engine.script_functions.insert(
|
|
|
|
FnSpec {
|
|
|
|
name: f.name.clone().into(),
|
|
|
|
args: None,
|
|
|
|
},
|
|
|
|
Arc::new(FnIntExt::Int(f.clone())),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
statements
|
|
|
|
};
|
2020-03-10 10:10:33 +01:00
|
|
|
|
|
|
|
let result = statements
|
|
|
|
.iter()
|
|
|
|
.try_fold(().into_dynamic(), |_, stmt| engine.eval_stmt(scope, stmt));
|
|
|
|
|
|
|
|
if !retain_functions {
|
|
|
|
engine.clear_functions();
|
|
|
|
}
|
|
|
|
|
|
|
|
result
|
2020-03-10 02:30:12 +01:00
|
|
|
}
|
2020-03-02 07:28:42 +01:00
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
match eval_ast_internal(self, scope, retain_functions, ast) {
|
2020-03-03 09:24:03 +01:00
|
|
|
Err(EvalAltResult::Return(out, pos)) => out.downcast::<T>().map(|v| *v).map_err(|a| {
|
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name((*a).type_name()).to_string(),
|
|
|
|
pos,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
|
|
|
|
Ok(out) => out.downcast::<T>().map(|v| *v).map_err(|a| {
|
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name((*a).type_name()).to_string(),
|
|
|
|
Position::eof(),
|
|
|
|
)
|
|
|
|
}),
|
2020-03-02 15:13:14 +01:00
|
|
|
|
2020-03-02 07:28:42 +01:00
|
|
|
Err(err) => Err(err),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Evaluate a file, but throw away the result and only return error (if any).
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
|
|
|
let mut f = File::open(filename)
|
2020-03-04 15:00:01 +01:00
|
|
|
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))?;
|
2020-03-02 07:28:42 +01:00
|
|
|
|
|
|
|
let mut contents = String::new();
|
|
|
|
|
|
|
|
f.read_to_string(&mut contents)
|
2020-03-04 15:00:01 +01:00
|
|
|
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(filename.into(), err))
|
2020-03-02 07:28:42 +01:00
|
|
|
.and_then(|_| self.consume(&contents))
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Evaluate a string, but throw away the result and only return error (if any).
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
2020-03-10 02:30:12 +01:00
|
|
|
self.consume_with_scope(&mut Scope::new(), false, input)
|
2020-03-02 07:28:42 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
/// Evaluate a string, but throw away the result and only return error (if any).
|
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
|
|
|
///
|
|
|
|
/// Note - if `retain_functions` is set to `true`, functions defined by previous scripts are _retained_
|
|
|
|
/// and not cleared from run to run.
|
2020-03-02 07:28:42 +01:00
|
|
|
pub fn consume_with_scope(
|
|
|
|
&mut self,
|
|
|
|
scope: &mut Scope,
|
2020-03-10 02:30:12 +01:00
|
|
|
retain_functions: bool,
|
2020-03-02 07:28:42 +01:00
|
|
|
input: &str,
|
|
|
|
) -> Result<(), EvalAltResult> {
|
|
|
|
let tokens = lex(input);
|
|
|
|
|
2020-03-09 14:57:07 +01:00
|
|
|
parse(&mut tokens.peekable(), self.optimize)
|
2020-03-02 07:28:42 +01:00
|
|
|
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
2020-03-11 06:28:12 +01:00
|
|
|
.and_then(|ast| {
|
|
|
|
#[cfg(feature = "no_function")]
|
|
|
|
let AST(statements) = ast;
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
let statements = {
|
|
|
|
let AST(ref statements, ref functions) = ast;
|
|
|
|
|
|
|
|
functions.iter().for_each(|f| {
|
|
|
|
self.script_functions.insert(
|
|
|
|
FnSpec {
|
|
|
|
name: f.name.clone().into(),
|
|
|
|
args: None,
|
|
|
|
},
|
|
|
|
Arc::new(FnIntExt::Int(f.clone())),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
statements
|
|
|
|
};
|
2020-03-02 07:28:42 +01:00
|
|
|
|
2020-03-04 16:06:05 +01:00
|
|
|
let val = statements
|
2020-03-02 07:28:42 +01:00
|
|
|
.iter()
|
2020-03-03 10:28:38 +01:00
|
|
|
.try_fold(().into_dynamic(), |_, o| self.eval_stmt(scope, o))
|
2020-03-02 07:28:42 +01:00
|
|
|
.map(|_| ());
|
|
|
|
|
2020-03-10 02:30:12 +01:00
|
|
|
if !retain_functions {
|
|
|
|
self.clear_functions();
|
|
|
|
}
|
2020-03-02 07:28:42 +01:00
|
|
|
|
|
|
|
val
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Call a script function defined in a compiled AST.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2020-03-09 14:57:07 +01:00
|
|
|
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
2020-03-10 12:48:47 +01:00
|
|
|
/// # #[cfg(not(feature = "no_stdlib"))]
|
2020-03-11 06:28:12 +01:00
|
|
|
/// # #[cfg(not(feature = "no_function"))]
|
2020-03-10 12:48:47 +01:00
|
|
|
/// # {
|
2020-03-09 14:57:07 +01:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-03-04 15:00:01 +01:00
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
2020-03-09 14:57:07 +01:00
|
|
|
/// let ast = engine.compile("fn add(x, y) { x.len() + y }")?;
|
2020-03-04 15:00:01 +01:00
|
|
|
///
|
2020-03-10 07:09:05 +01:00
|
|
|
/// let result: i64 = engine.call_fn("add", &ast, (String::from("abc"), 123_i64))?;
|
2020-03-04 15:00:01 +01:00
|
|
|
///
|
|
|
|
/// assert_eq!(result, 126);
|
2020-03-10 12:48:47 +01:00
|
|
|
/// # }
|
2020-03-04 15:00:01 +01:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-11 06:28:12 +01:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-03-10 07:09:05 +01:00
|
|
|
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
|
2020-03-04 15:00:01 +01:00
|
|
|
&mut self,
|
|
|
|
name: &str,
|
2020-03-06 17:29:45 +01:00
|
|
|
ast: &AST,
|
2020-03-04 15:00:01 +01:00
|
|
|
args: A,
|
2020-03-10 07:09:05 +01:00
|
|
|
) -> Result<T, EvalAltResult> {
|
2020-03-10 10:10:33 +01:00
|
|
|
fn call_fn_internal(
|
|
|
|
engine: &mut Engine,
|
|
|
|
name: &str,
|
|
|
|
ast: &AST,
|
|
|
|
args: FnCallArgs,
|
|
|
|
) -> Result<Dynamic, EvalAltResult> {
|
|
|
|
ast.1.iter().for_each(|f| {
|
|
|
|
engine.script_functions.insert(
|
|
|
|
FnSpec {
|
|
|
|
name: f.name.clone().into(),
|
|
|
|
args: None,
|
|
|
|
},
|
|
|
|
Arc::new(FnIntExt::Int(f.clone())),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
let result = engine.call_fn_raw(name, args, None, Position::none());
|
|
|
|
|
|
|
|
engine.clear_functions();
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2020-03-10 07:09:05 +01:00
|
|
|
let mut arg_values = args.into_vec();
|
|
|
|
|
2020-03-10 10:10:33 +01:00
|
|
|
call_fn_internal(
|
|
|
|
self,
|
2020-03-10 07:09:05 +01:00
|
|
|
name,
|
|
|
|
ast,
|
|
|
|
arg_values.iter_mut().map(|v| v.as_mut()).collect(),
|
|
|
|
)
|
2020-03-10 10:10:33 +01:00
|
|
|
.and_then(|b| {
|
2020-03-10 07:09:05 +01:00
|
|
|
b.downcast().map(|b| *b).map_err(|a| {
|
|
|
|
EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
self.map_type_name((*a).type_name()).into(),
|
2020-03-10 10:10:33 +01:00
|
|
|
Position::none(),
|
2020-03-10 07:09:05 +01:00
|
|
|
)
|
|
|
|
})
|
2020-03-10 10:10:33 +01:00
|
|
|
})
|
2020-03-04 15:00:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Override default action of `print` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2020-03-09 14:57:07 +01:00
|
|
|
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-03-04 15:00:01 +01:00
|
|
|
/// let mut result = String::from("");
|
|
|
|
/// {
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'print' function
|
|
|
|
/// engine.on_print(|s| result.push_str(s));
|
2020-03-09 14:57:07 +01:00
|
|
|
/// engine.consume("print(40 + 2);")?;
|
2020-03-04 15:00:01 +01:00
|
|
|
/// }
|
|
|
|
/// assert_eq!(result, "42");
|
2020-03-09 14:57:07 +01:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
2020-03-04 15:00:01 +01:00
|
|
|
/// ```
|
2020-03-06 17:29:45 +01:00
|
|
|
pub fn on_print(&mut self, callback: impl FnMut(&str) + 'e) {
|
2020-03-02 07:28:42 +01:00
|
|
|
self.on_print = Box::new(callback);
|
|
|
|
}
|
|
|
|
|
2020-03-04 15:00:01 +01:00
|
|
|
/// Override default action of `debug` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
2020-03-09 14:57:07 +01:00
|
|
|
/// # fn main() -> Result<(), rhai::EvalAltResult> {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-03-04 15:00:01 +01:00
|
|
|
/// let mut result = String::from("");
|
|
|
|
/// {
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'debug' function
|
|
|
|
/// engine.on_debug(|s| result.push_str(s));
|
2020-03-09 14:57:07 +01:00
|
|
|
/// engine.consume(r#"debug("hello");"#)?;
|
2020-03-04 15:00:01 +01:00
|
|
|
/// }
|
|
|
|
/// assert_eq!(result, "\"hello\"");
|
2020-03-09 14:57:07 +01:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
2020-03-04 15:00:01 +01:00
|
|
|
/// ```
|
2020-03-06 17:29:45 +01:00
|
|
|
pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'e) {
|
2020-03-02 07:28:42 +01:00
|
|
|
self.on_debug = Box::new(callback);
|
|
|
|
}
|
|
|
|
}
|