diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index e7e02408..1e59b3e7 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -121,6 +121,7 @@ The Rhai Scripting Language 5. [Multi-Layer Functions](patterns/multi-layer.md) 6. [One Engine Instance Per Call](patterns/parallel.md) 7. [Scriptable Event Handler with State](patterns/events.md) + 8. [Dynamic Constants Provider](patterns/dynamic-const.md) 9. [Advanced Topics](advanced.md) 1. [Capture Scope for Function Call](language/fn-capture.md) 2. [Low-Level API](rust/register-raw.md) diff --git a/doc/src/advanced.md b/doc/src/advanced.md index 7ed51620..87136de0 100644 --- a/doc/src/advanced.md +++ b/doc/src/advanced.md @@ -3,14 +3,4 @@ Advanced Topics {{#include links.md}} -This section covers advanced features such as: - -* [Advanced patterns]({{rootUrl}}/patterns/index.md) in using Rhai. - -* [Capture the calling scope]({{rootUrl}}/language/fn-capture.md) in a function call. - -* [`serde`] integration. - -* Low-level [function registration API]({{rootUrl}}/rust/register-raw.md) - -* [Domain-Specific Languages][DSL]. +This section covers advanced features of the Rhai [`Engine`]. diff --git a/doc/src/engine/custom-syntax.md b/doc/src/engine/custom-syntax.md index 645c0684..f2a76db1 100644 --- a/doc/src/engine/custom-syntax.md +++ b/doc/src/engine/custom-syntax.md @@ -122,7 +122,7 @@ where: * `context: &mut EvalContext` - mutable reference to the current evaluation _context_ (**do not touch**) which exposes the following fields: * `context.engine(): &Engine` - reference to the current [`Engine`]. - * `context.namespace(): &Module` - reference to the current _global namespace_ containing all script-defined functions. + * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions. * `context.call_level(): usize` - the current nesting level of function calls. * `inputs: &[Expression]` - a list of input expression trees. diff --git a/doc/src/engine/var.md b/doc/src/engine/var.md index 6e9df16f..bcd75069 100644 --- a/doc/src/engine/var.md +++ b/doc/src/engine/var.md @@ -16,7 +16,7 @@ To do so, provide a closure to the [`Engine`] via the [`Engine::on_var`] method: let mut engine = Engine::new(); // Register a variable resolver. -engine.on_var(|name, index, engine, scope, lib| { +engine.on_var(|name, index, scope, context| { match name { "MYSTIC_NUMBER" => Ok(Some((42 as INT).into())), // Override a variable - make it not found even if it exists! @@ -50,26 +50,23 @@ Function Signature The function signature passed to `Engine::on_var` takes the following form: -> `Fn(name: &str, index: Option, scope: &Scope, context: &EvalContext) -> Result, Box> + 'static` +> `Fn(name: &str, index: usize, scope: &Scope, context: &EvalContext) -> Result, Box> + 'static` where: * `name: &str` - variable name. -* `index: Option` - an offset from the bottom of the current [`Scope`] that the variable is supposed to reside. +* `index: usize` - an offset from the bottom of the current [`Scope`] that the variable is supposed to reside. Offsets start from 1, with 1 meaning the last variable in the current [`Scope`]. Essentially the correct variable is at position `scope.len() - index`. - Notice that, if there was an [`eval`] statement before the current statement, new variables may have been introduced and this index may be incorrect. - Therefore, this index is for reference only. It should not be relied upon. - If `index` is `None`, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. + If `index` is zero, then there is no pre-calculated offset position and a search through the current [`Scope`] must be performed. * `scope : &Scope` - reference to the current [`Scope`] containing all variables up to the current evaluation position. * `context: &EvalContext` - reference to the current evaluation _context_, which exposes the following fields: * `context.engine(): &Engine` - reference to the current [`Engine`]. - * `context.namespace(): &Module` - reference to the current _global namespace_ containing all script-defined functions. + * `context.namespace(): &Module` - reference to the current _global namespace_ (as a [module]) containing all script-defined functions. * `context.call_level(): usize` - the current nesting level of function calls. The return value is `Result, Box>` where `Ok(None)` indicates that the normal variable resolution process should continue. - diff --git a/doc/src/patterns/dynamic-const.md b/doc/src/patterns/dynamic-const.md new file mode 100644 index 00000000..b370e099 --- /dev/null +++ b/doc/src/patterns/dynamic-const.md @@ -0,0 +1,56 @@ +Dynamic Constants Provider +========================= + +{{#include ../links.md}} + + +Usage Scenario +-------------- + +* A system has a _large_ number of constants, but only a minor set will be used by any script. + +* The system constants are expensive to load. + +* The system constants set is too massive to push into a custom [`Scope`]. + +* The values of system constants are volatile and call-dependent. + + +Key Concepts +------------ + +* Use a [variable resolver] to intercept variable access. + +* Only load a variable when it is being used. + +* Perform a lookup based on variable name, and provide the correct data value. + +* May even perform back-end network access or look up the latest value from a database. + + +Implementation +-------------- + +```rust +let mut engine = Engine::new(); + +// Create shared data provider. +// Assume that Provider::get(&str) -> Option gets a system constant. +let provider: Arc = get_data_provider(); + +// Clone the shared provider +let db = provider.clone(); + +// Register a variable resolver. +// Move the shared provider into the closure. +engine.on_var(move |name, _, _, _| Ok(db.get(name).map(Dynamic::from))); +``` + + +Values are Constants +-------------------- + +All values provided by a [variable resolver] are _constants_ due to their dynamic nature. +They cannot be assigned to. + +In order to change values in an external system, register a dedicated API for that purpose. diff --git a/src/api.rs b/src/api.rs index 7b7e6d3d..f07b05c0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1721,12 +1721,7 @@ impl Engine { #[inline(always)] pub fn on_var( &mut self, - callback: impl Fn( - &str, - Option, - &Scope, - &EvalContext, - ) -> Result, Box> + callback: impl Fn(&str, usize, &Scope, &EvalContext) -> Result, Box> + SendSync + 'static, ) -> &mut Self { diff --git a/src/engine.rs b/src/engine.rs index 9b5f4a53..5c47a3cf 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -745,7 +745,7 @@ impl Engine { this_ptr, level: 0, }; - if let Some(result) = resolve_var(name, index.map(|v| v.get()), scope, &context) + if let Some(result) = resolve_var(name, index.map_or(0, |v| v.get()), scope, &context) .map_err(|err| err.fill_position(*pos))? { return Ok((result.into(), name, ScopeEntryType::Constant, *pos)); diff --git a/src/fn_native.rs b/src/fn_native.rs index 8cef4998..3aa5e7a4 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -224,13 +224,13 @@ pub type Callback = Box R + Send + Sync + 'static>; /// A standard callback function. #[cfg(not(feature = "sync"))] pub type OnVarCallback = Box< - dyn Fn(&str, Option, &Scope, &EvalContext) -> Result, Box> + dyn Fn(&str, usize, &Scope, &EvalContext) -> Result, Box> + 'static, >; /// A standard callback function. #[cfg(feature = "sync")] pub type OnVarCallback = Box< - dyn Fn(&str, Option, &Scope, &EvalContext) -> Result, Box> + dyn Fn(&str, usize, &Scope, &EvalContext) -> Result, Box> + Send + Sync + 'static,