118 lines
2.8 KiB
Markdown
118 lines
2.8 KiB
Markdown
Multi-Layer Functions
|
|
=====================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
|
|
Usage Scenario
|
|
--------------
|
|
|
|
* A system is divided into separate _layers_, each providing logic in terms of scripted [functions].
|
|
|
|
* A lower layer provides _default implementations_ of certain functions.
|
|
|
|
* Higher layers each provide progressively more specific implementations of the same functions.
|
|
|
|
* A more specific function, if defined in a higher layer, always overrides the implementation in a lower layer.
|
|
|
|
* This is akin to object-oriented programming but with functions.
|
|
|
|
* This type of system is extremely convenient for dynamic business rules configuration, setting corporate-wide
|
|
policies, granting permissions for specific roles etc. where specific, local rules need to override
|
|
corporate-wide defaults.
|
|
|
|
|
|
Key Concepts
|
|
------------
|
|
|
|
* Each layer is a separate script.
|
|
|
|
* The lowest layer script is compiled into a base [`AST`].
|
|
|
|
* Higher layer scripts are also compiled into [`AST`] and _combined_ into the base using `AST::combine`
|
|
(or the `+=` operator), overriding any existing functions.
|
|
|
|
|
|
Examples
|
|
--------
|
|
|
|
Assume the following four scripts:
|
|
|
|
```rust
|
|
----------------
|
|
| default.rhai |
|
|
----------------
|
|
|
|
// Default implementation of 'foo'.
|
|
fn foo(x) { x + 1 }
|
|
|
|
// Default implementation of 'bar'.
|
|
fn bar(x, y) { x + y }
|
|
|
|
// Default implementation of 'no_touch'.
|
|
fn no_touch() { throw "do not touch me!"; }
|
|
|
|
|
|
---------------
|
|
| lowest.rhai |
|
|
---------------
|
|
|
|
// Specific implementation of 'foo'.
|
|
fn foo(x) { x * 2 }
|
|
|
|
// New implementation for this layer.
|
|
fn baz() { print("hello!"); }
|
|
|
|
|
|
---------------
|
|
| middle.rhai |
|
|
---------------
|
|
|
|
// Specific implementation of 'bar'.
|
|
fn bar(x, y) { x - y }
|
|
|
|
// Specific implementation of 'baz'.
|
|
fn baz() { print("hey!"); }
|
|
|
|
|
|
----------------
|
|
| highest.rhai |
|
|
----------------
|
|
|
|
// Specific implementation of 'foo'.
|
|
fn foo(x) { x + 42 }
|
|
```
|
|
|
|
Load and combine them sequentially:
|
|
|
|
```rust
|
|
let engine = Engine::new();
|
|
|
|
// Compile the baseline default implementations.
|
|
let mut ast = engine.compile_file("default.rhai".into())?;
|
|
|
|
// Combine the first layer.
|
|
let lowest = engine.compile_file("lowest.rhai".into())?;
|
|
ast += lowest;
|
|
|
|
// Combine the second layer.
|
|
let middle = engine.compile_file("middle.rhai".into())?;
|
|
ast += lowest;
|
|
|
|
// Combine the third layer.
|
|
let highest = engine.compile_file("highest.rhai".into())?;
|
|
ast += lowest;
|
|
|
|
// Now, 'ast' contains the following functions:
|
|
//
|
|
// fn no_touch() { // from 'default.rhai'
|
|
// throw "do not touch me!";
|
|
// }
|
|
// fn foo(x) { x + 42 } // from 'highest.rhai'
|
|
// fn bar(x, y) { x - y } // from 'middle.rhai'
|
|
// fn baz() { print("hey!"); } // from 'middle.rhai'
|
|
```
|
|
|
|
Unfortunately, there is no `super` call that calls the base implementation
|
|
(i.e. no way for a higher-layer function to call an equivalent lower-layer function).
|