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}