rhai/api/
events.rs

1//! Module that defines public event handlers for [`Engine`].
2
3use crate::func::SendSync;
4use crate::{Dynamic, Engine, EvalContext, Position, RhaiResultOf, VarDefInfo};
5#[cfg(feature = "no_std")]
6use std::prelude::v1::*;
7
8impl Engine {
9    /// Provide a callback that will be invoked before each variable access.
10    ///
11    /// # WARNING - Unstable API
12    ///
13    /// This API is volatile and may change in the future.
14    ///
15    /// # Callback Function Signature
16    ///
17    /// `Fn(name: &str, index: usize, context: EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>`
18    ///
19    /// where:
20    /// * `name`: name of the variable.
21    /// * `index`: an offset from the bottom of the current [`Scope`][crate::Scope] that the
22    ///   variable is supposed to reside. Offsets start from 1, with 1 meaning the last variable in
23    ///   the current [`Scope`][crate::Scope].  Essentially the correct variable is at position
24    ///   `scope.len() - index`. If `index` is zero, then there is no pre-calculated offset position
25    ///   and a search through the current [`Scope`][crate::Scope] must be performed.
26    /// * `context`: the current [evaluation context][`EvalContext`].
27    ///
28    /// ## Return value
29    ///
30    /// * `Ok(None)`: continue with normal variable access.
31    /// * `Ok(Some(Dynamic))`: the variable's value.
32    ///
33    /// ## Raising errors
34    ///
35    /// Return `Err(...)` if there is an error.
36    ///
37    /// # Example
38    ///
39    /// ```
40    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
41    /// use rhai::Engine;
42    ///
43    /// let mut engine = Engine::new();
44    ///
45    /// // Register a variable resolver.
46    /// engine.on_var(|name, _, _| {
47    ///     match name {
48    ///         "MYSTIC_NUMBER" => Ok(Some(42_i64.into())),
49    ///         _ => Ok(None)
50    ///     }
51    /// });
52    ///
53    /// engine.eval::<i64>("MYSTIC_NUMBER")?;
54    ///
55    /// # Ok(())
56    /// # }
57    /// ```
58    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
59    #[inline(always)]
60    pub fn on_var(
61        &mut self,
62        callback: impl Fn(&str, usize, EvalContext) -> RhaiResultOf<Option<Dynamic>>
63            + SendSync
64            + 'static,
65    ) -> &mut Self {
66        self.resolve_var = Some(Box::new(callback));
67        self
68    }
69    /// Provide a callback that will be invoked before the definition of each variable .
70    ///
71    /// # WARNING - Unstable API
72    ///
73    /// This API is volatile and may change in the future.
74    ///
75    /// # Callback Function Signature
76    ///
77    /// `Fn(is_runtime: bool, info: VarInfo, context: EvalContext) -> Result<bool, Box<EvalAltResult>>`
78    ///
79    /// where:
80    /// * `is_runtime`: `true` if the variable definition event happens during runtime, `false` if during compilation.
81    /// * `info`: information on the variable.
82    /// * `context`: the current [evaluation context][`EvalContext`].
83    ///
84    /// ## Return value
85    ///
86    /// * `Ok(true)`: continue with normal variable definition.
87    /// * `Ok(false)`: deny the variable definition with an [runtime error][crate::EvalAltResult::ErrorRuntime].
88    ///
89    /// ## Raising errors
90    ///
91    /// Return `Err(...)` if there is an error.
92    ///
93    /// # Example
94    ///
95    /// ```should_panic
96    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
97    /// use rhai::Engine;
98    ///
99    /// let mut engine = Engine::new();
100    ///
101    /// // Register a variable definition filter.
102    /// engine.on_def_var(|_, info, _| {
103    ///     // Disallow defining MYSTIC_NUMBER as a constant
104    ///     if info.name() == "MYSTIC_NUMBER" && info.is_const() {
105    ///         Ok(false)
106    ///     } else {
107    ///         Ok(true)
108    ///     }
109    /// });
110    ///
111    /// // The following runs fine:
112    /// engine.eval::<i64>("let MYSTIC_NUMBER = 42;")?;
113    ///
114    /// // The following will cause an error:
115    /// engine.eval::<i64>("const MYSTIC_NUMBER = 42;")?;
116    ///
117    /// # Ok(())
118    /// # }
119    /// ```
120    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
121    #[inline(always)]
122    pub fn on_def_var(
123        &mut self,
124        callback: impl Fn(bool, VarDefInfo, EvalContext) -> RhaiResultOf<bool> + SendSync + 'static,
125    ) -> &mut Self {
126        self.def_var_filter = Some(Box::new(callback));
127        self
128    }
129    /// _(internals)_ Register a callback that will be invoked during parsing to remap certain tokens.
130    /// Exported under the `internals` feature only.
131    ///
132    /// # WARNING - Unstable API
133    ///
134    /// This API is volatile and may change in the future.
135    ///
136    /// # Callback Function Signature
137    ///
138    /// `Fn(token: Token, pos: Position, state: &TokenizeState) -> Token`
139    ///
140    /// where:
141    /// * [`token`][crate::tokenizer::Token]: current token parsed
142    /// * [`pos`][`Position`]: location of the token
143    /// * [`state`][crate::tokenizer::TokenizeState]: current state of the tokenizer
144    ///
145    /// ## Raising errors
146    ///
147    /// It is possible to raise a parsing error by returning
148    /// [`Token::LexError`][crate::tokenizer::Token::LexError] as the mapped token.
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
154    /// use rhai::{Engine, Token};
155    ///
156    /// let mut engine = Engine::new();
157    ///
158    /// // Register a token mapper.
159    /// # #[allow(deprecated)]
160    /// engine.on_parse_token(|token, _, _| {
161    ///     match token {
162    ///         // Convert all integer literals to strings
163    ///         Token::IntegerConstant(n) => Token::StringConstant(Box::new(n.to_string().into())),
164    ///         // Convert 'begin' .. 'end' to '{' .. '}'
165    ///         Token::Identifier(s) if &*s == "begin" => Token::LeftBrace,
166    ///         Token::Identifier(s) if &*s == "end" => Token::RightBrace,
167    ///         // Pass through all other tokens unchanged
168    ///         _ => token
169    ///     }
170    /// });
171    ///
172    /// assert_eq!(engine.eval::<String>("42")?, "42");
173    /// assert_eq!(engine.eval::<bool>("true")?, true);
174    /// assert_eq!(engine.eval::<String>("let x = 42; begin let x = 0; end; x")?, "42");
175    ///
176    /// # Ok(())
177    /// # }
178    /// ```
179    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
180    #[cfg(feature = "internals")]
181    #[inline(always)]
182    pub fn on_parse_token(
183        &mut self,
184        callback: impl Fn(
185                crate::tokenizer::Token,
186                Position,
187                &crate::tokenizer::TokenizeState,
188            ) -> crate::tokenizer::Token
189            + SendSync
190            + 'static,
191    ) -> &mut Self {
192        self.token_mapper = Some(Box::new(callback));
193        self
194    }
195    /// Register a callback for script evaluation progress.
196    ///
197    /// Not available under `unchecked`.
198    ///
199    /// # Callback Function Signature
200    ///
201    /// `Fn(counter: u64) -> Option<Dynamic>`
202    ///
203    /// ## Return value
204    ///
205    /// * `None`: continue running the script.
206    /// * `Some(Dynamic)`: terminate the script with the specified exception value.
207    ///
208    /// # Example
209    ///
210    /// ```
211    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
212    /// # use std::sync::RwLock;
213    /// # use std::sync::Arc;
214    /// use rhai::Engine;
215    ///
216    /// let result = Arc::new(RwLock::new(0_u64));
217    /// let logger = result.clone();
218    ///
219    /// let mut engine = Engine::new();
220    ///
221    /// engine.on_progress(move |ops| {
222    ///     if ops > 1000 {
223    ///         Some("Over 1,000 operations!".into())
224    ///     } else if ops % 123 == 0 {
225    ///         *logger.write().unwrap() = ops;
226    ///         None
227    ///     } else {
228    ///         None
229    ///     }
230    /// });
231    ///
232    /// engine.run("for x in 0..5000 { print(x); }")
233    ///       .expect_err("should error");
234    ///
235    /// assert_eq!(*result.read().unwrap(), 984);
236    ///
237    /// # Ok(())
238    /// # }
239    /// ```
240    #[cfg(not(feature = "unchecked"))]
241    #[inline(always)]
242    pub fn on_progress(
243        &mut self,
244        callback: impl Fn(u64) -> Option<Dynamic> + SendSync + 'static,
245    ) -> &mut Self {
246        self.progress = Some(Box::new(callback));
247        self
248    }
249    /// Override default action of `print` (print to stdout using [`println!`])
250    ///
251    /// # Example
252    ///
253    /// ```
254    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
255    /// # use std::sync::RwLock;
256    /// # use std::sync::Arc;
257    /// use rhai::Engine;
258    ///
259    /// let result = Arc::new(RwLock::new(String::new()));
260    ///
261    /// let mut engine = Engine::new();
262    ///
263    /// // Override action of 'print' function
264    /// let logger = result.clone();
265    /// engine.on_print(move |s| logger.write().unwrap().push_str(s));
266    ///
267    /// engine.run("print(40 + 2);")?;
268    ///
269    /// assert_eq!(*result.read().unwrap(), "42");
270    /// # Ok(())
271    /// # }
272    /// ```
273    #[inline(always)]
274    pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
275        self.print = Some(Box::new(callback));
276        self
277    }
278    /// Override default action of `debug` (print to stdout using [`println!`])
279    ///
280    /// # Callback Function Signature
281    ///
282    /// The callback function signature passed takes the following form:
283    ///
284    /// `Fn(text: &str, source: Option<&str>, pos: Position)`
285    ///
286    /// where:
287    /// * `text`: the text to display
288    /// * `source`: current source, if any
289    /// * [`pos`][`Position`]: location of the `debug` call
290    ///
291    /// # Example
292    ///
293    /// ```
294    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
295    /// # use std::sync::RwLock;
296    /// # use std::sync::Arc;
297    /// use rhai::Engine;
298    ///
299    /// let result = Arc::new(RwLock::new(String::new()));
300    ///
301    /// let mut engine = Engine::new();
302    ///
303    /// // Override action of 'print' function
304    /// let logger = result.clone();
305    /// engine.on_debug(move |s, src, pos| logger.write().unwrap().push_str(
306    ///                     &format!("{} @ {:?} > {}", src.unwrap_or("unknown"), pos, s)
307    ///                ));
308    ///
309    /// let mut ast = engine.compile(r#"let x = "hello"; debug(x);"#)?;
310    /// ast.set_source("world");
311    /// engine.run_ast(&ast)?;
312    ///
313    /// #[cfg(not(feature = "no_position"))]
314    /// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#);
315    /// #[cfg(feature = "no_position")]
316    /// assert_eq!(*result.read().unwrap(), r#"world @ none > "hello""#);
317    /// # Ok(())
318    /// # }
319    /// ```
320    #[inline(always)]
321    pub fn on_debug(
322        &mut self,
323        callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static,
324    ) -> &mut Self {
325        self.debug = Some(Box::new(callback));
326        self
327    }
328    /// _(internals)_ Register a callback for access to [`Map`][crate::Map] properties that do not exist.
329    /// Exported under the `internals` feature only.
330    ///
331    /// Not available under `no_index`.
332    ///
333    /// # WARNING - Unstable API
334    ///
335    /// This API is volatile and may change in the future.
336    ///
337    /// # Callback Function Signature
338    ///
339    /// `Fn(array: &mut Array, index: INT) -> Result<Target, Box<EvalAltResult>>`
340    ///
341    /// where:
342    /// * `array`: mutable reference to the [`Array`][crate::Array] instance.
343    /// * `index`: numeric index of the array access.
344    ///
345    /// ## Return value
346    ///
347    /// * `Ok(Target)`: [`Target`][crate::Target] of the indexing access.
348    ///
349    /// ## Raising errors
350    ///
351    /// Return `Err(...)` if there is an error, usually
352    /// [`EvalAltResult::ErrorPropertyNotFound`][crate::EvalAltResult::ErrorPropertyNotFound].
353    ///
354    /// # Example
355    ///
356    /// ```
357    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
358    /// # use rhai::{Engine, Dynamic, EvalAltResult, Position};
359    /// # use std::convert::TryInto;
360    /// let mut engine = Engine::new();
361    ///
362    /// engine.on_invalid_array_index(|arr, index, _| match index
363    /// {
364    ///     -100 => {
365    ///         // The array can be modified in place
366    ///         arr.push((42_i64).into());
367    ///         // Return a mutable reference to an element
368    ///         let value_ref = arr.last_mut().unwrap();
369    ///         value_ref.try_into()
370    ///     }
371    ///     100 => {
372    ///         let value = Dynamic::from(100_i64);
373    ///         // Return a temporary value (not a reference)
374    ///         Ok(value.into())
375    ///     }
376    ///     // Return the standard out-of-bounds error
377    ///     _ => Err(EvalAltResult::ErrorArrayBounds(
378    ///                 arr.len(), index, Position::NONE
379    ///          ).into()),
380    /// });
381    ///
382    /// let r = engine.eval::<i64>("
383    ///             let a = [1, 2, 3];
384    ///             a[-100] += 1;
385    ///             a[3] + a[100]
386    ///         ")?;
387    ///
388    /// assert_eq!(r, 143);
389    /// # Ok(()) }
390    /// ```
391    #[cfg(not(feature = "no_index"))]
392    #[cfg(feature = "internals")]
393    #[inline(always)]
394    pub fn on_invalid_array_index(
395        &mut self,
396        callback: impl for<'a> Fn(
397                &'a mut crate::Array,
398                crate::INT,
399                EvalContext,
400            ) -> RhaiResultOf<crate::Target<'a>>
401            + SendSync
402            + 'static,
403    ) -> &mut Self {
404        self.invalid_array_index = Some(Box::new(callback));
405        self
406    }
407    /// _(internals)_ Register a callback for access to [`Map`][crate::Map] properties that do not exist.
408    /// Exported under the `internals` feature only.
409    ///
410    /// Not available under `no_object`.
411    ///
412    /// # WARNING - Unstable API
413    ///
414    /// This API is volatile and may change in the future.
415    ///
416    /// # Callback Function Signature
417    ///
418    /// `Fn(map: &mut Map, prop: &str) -> Result<Target, Box<EvalAltResult>>`
419    ///
420    /// where:
421    /// * `map`: mutable reference to the [`Map`][crate::Map] instance.
422    /// * `prop`: name of the property that does not exist.
423    ///
424    /// ## Return value
425    ///
426    /// * `Ok(Target)`: [`Target`][crate::Target] of the property access.
427    ///
428    /// ## Raising errors
429    ///
430    /// Return `Err(...)` if there is an error, usually [`EvalAltResult::ErrorPropertyNotFound`][crate::EvalAltResult::ErrorPropertyNotFound].
431    ///
432    /// # Example
433    ///
434    /// ```
435    /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
436    /// # use rhai::{Engine, Dynamic, EvalAltResult, Position};
437    /// # use std::convert::TryInto;
438    /// let mut engine = Engine::new();
439    ///
440    /// engine.on_map_missing_property(|map, prop, _| match prop
441    /// {
442    ///     "x" => {
443    ///         // The object-map can be modified in place
444    ///         map.insert("y".into(), (42_i64).into());
445    ///         // Return a mutable reference to an element
446    ///         let value_ref = map.get_mut("y").unwrap();
447    ///         value_ref.try_into()
448    ///     }
449    ///     "z" => {
450    ///         // Return a temporary value (not a reference)
451    ///         let value = Dynamic::from(100_i64);
452    ///         Ok(value.into())
453    ///     }
454    ///     // Return the standard property-not-found error
455    ///     _ => Err(EvalAltResult::ErrorPropertyNotFound(
456    ///                 prop.to_string(), Position::NONE
457    ///          ).into()),
458    /// });
459    ///
460    /// let r = engine.eval::<i64>("
461    ///             let obj = #{ a:1, b:2 };
462    ///             obj.x += 1;
463    ///             obj.y + obj.z
464    ///         ")?;
465    ///
466    /// assert_eq!(r, 143);
467    /// # Ok(()) }
468    /// ```
469    #[cfg(not(feature = "no_object"))]
470    #[cfg(feature = "internals")]
471    #[inline(always)]
472    pub fn on_map_missing_property(
473        &mut self,
474        callback: impl for<'a> Fn(&'a mut crate::Map, &str, EvalContext) -> RhaiResultOf<crate::Target<'a>>
475            + SendSync
476            + 'static,
477    ) -> &mut Self {
478        self.missing_map_property = Some(Box::new(callback));
479        self
480    }
481    /// _(debugging)_ Register a callback for debugging.
482    /// Exported under the `debugging` feature only.
483    ///
484    /// # WARNING - Unstable API
485    ///
486    /// This API is volatile and may change in the future.
487    #[deprecated = "This API is NOT deprecated, but it is considered volatile and may change in the future."]
488    #[cfg(feature = "debugging")]
489    #[inline(always)]
490    pub fn register_debugger(
491        &mut self,
492        init: impl Fn(&Self, crate::debugger::Debugger) -> crate::debugger::Debugger
493            + SendSync
494            + 'static,
495        callback: impl Fn(
496                EvalContext,
497                crate::eval::DebuggerEvent,
498                crate::ast::ASTNode,
499                Option<&str>,
500                Position,
501            ) -> RhaiResultOf<crate::eval::DebuggerCommand>
502            + SendSync
503            + 'static,
504    ) -> &mut Self {
505        self.debugger_interface = Some((Box::new(init), Box::new(callback)));
506        self
507    }
508}