Add Engine::load_module.
This commit is contained in:
parent
fbe9425794
commit
937b45a187
@ -9,48 +9,143 @@ it is impossible (short of registering a complete API) to distinguish between in
|
|||||||
enum variants or to extract internal data from them.
|
enum variants or to extract internal data from them.
|
||||||
|
|
||||||
|
|
||||||
Switch Through Arrays
|
Simulate an Enum API
|
||||||
---------------------
|
--------------------
|
||||||
|
|
||||||
An easy way to work with Rust enums is through exposing the internal data of each enum variant
|
|
||||||
as an [array], usually with the name of the variant as the first item:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Array};
|
use rhai::{Engine, RegisterFn, Dynamic, EvalAltResult};
|
||||||
|
use rhai::plugin::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
enum MyEnum {
|
enum MyEnum {
|
||||||
Foo,
|
Foo,
|
||||||
Bar(i64),
|
Bar(i64),
|
||||||
Baz(String, bool)
|
Baz(String, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MyEnum {
|
// Create a plugin module with functions constructing the 'MyEnum' variants
|
||||||
fn get_enum_data(&mut self) -> Array {
|
#[export_module]
|
||||||
match self {
|
pub mod MyEnumModule {
|
||||||
Self::Foo => vec![
|
// 'MyEnum' variants
|
||||||
"Foo".into()
|
pub const Foo: &MyEnum = MyEnum::Foo;
|
||||||
] as Array,
|
pub fn Bar(value: i64) -> MyEnum { MyEnum::Bar(value) }
|
||||||
Self::Bar(num) => vec![
|
pub fn Baz(val1: String, val2: bool) -> MyEnum { MyEnum::Baz(val1, val2) }
|
||||||
"Bar".into(), (*num).into()
|
|
||||||
] as Array,
|
|
||||||
Self::Baz(name, option) => vec![
|
|
||||||
"Baz".into(), name.clone().into(), (*option).into()
|
|
||||||
] as Array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// Register API for 'MyEnum'
|
||||||
engine
|
engine
|
||||||
|
// Register enum custom type
|
||||||
.register_type_with_name::<MyEnum>("MyEnum")
|
.register_type_with_name::<MyEnum>("MyEnum")
|
||||||
.register_get("enum_data", MyEnum::get_enum_data);
|
// Register access to fields
|
||||||
|
.register_get("type", |a: &mut MyEnum| match a {
|
||||||
|
MyEnum::Foo => "Foo".to_string(),
|
||||||
|
MyEnum::Bar(_) => "Bar".to_string(),
|
||||||
|
MyEnum::Baz(_, _) => "Baz".to_string()
|
||||||
|
})
|
||||||
|
.register_get("field_0", |a: &mut MyEnum| match a {
|
||||||
|
MyEnum::Foo => Dynamic::UNIT,
|
||||||
|
MyEnum::Bar(x) => Dynamic::from(x),
|
||||||
|
MyEnum::Baz(x, _) => Dynamic::from(x)
|
||||||
|
})
|
||||||
|
.register_get("field_1", |a: &mut MyEnum| match a {
|
||||||
|
MyEnum::Foo | MyEnum::Bar(_) => Dynamic::UNIT,
|
||||||
|
MyEnum::Baz(_, x) => Dynamic::from(x)
|
||||||
|
})
|
||||||
|
// Register printing
|
||||||
|
.register_fn("to_string", |a: &mut MyEnum| format!("{:?}", a))
|
||||||
|
.register_fn("print", |a: &mut MyEnum| format!("{:?}", a))
|
||||||
|
.register_fn("debug", |a: &mut MyEnum| format!("{:?}", a))
|
||||||
|
.register_fn("+", |s: &str, a: MyEnum| format!("{}{:?}", s, a))
|
||||||
|
.register_fn("+", |a: &mut MyEnum, s: &str| format!("{:?}", a).push_str(s))
|
||||||
|
.register_fn("+=", |s: &mut ImmutableString, a: MyEnum| s += a.to_string())
|
||||||
|
// Register '==' and '!=' operators
|
||||||
|
.register_fn("==", |a: &mut MyEnum, b: MyEnum| a == &b)
|
||||||
|
.register_fn("!=", |a: &mut MyEnum, b: MyEnum| a != &b)
|
||||||
|
// Register array functions
|
||||||
|
.register_fn("push", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item)))
|
||||||
|
.register_fn("+=", |list: &mut Array, item: MyEnum| list.push(Dynamic::from(item)))
|
||||||
|
.register_fn("insert", |list: &mut Array, position: i64, item: MyEnum| {
|
||||||
|
if position <= 0 {
|
||||||
|
list.insert(0, Dynamic::from(item));
|
||||||
|
} else if (position as usize) >= list.len() - 1 {
|
||||||
|
list.push(item);
|
||||||
|
} else {
|
||||||
|
list.insert(position as usize, Dynamic::from(item));
|
||||||
|
}
|
||||||
|
}).register_fn("pad", |list: &mut Array, len: i64, item: MyEnum| {
|
||||||
|
if len as usize > list.len() { list.resize(len as usize, item); }
|
||||||
|
})
|
||||||
|
// Load the module as the module namespace "MyEnum"
|
||||||
|
.register_module("MyEnum", exported_module!(MyEnumModule));
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of registering all these manually, it is often convenient to wrap them up into
|
||||||
|
a [custom package] that can be loaded into any [`Engine`].
|
||||||
|
|
||||||
|
With this API in place, working with enums will be almost the same as in Rust:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = MyEnum::Foo;
|
||||||
|
|
||||||
|
let y = MyEnum::Bar(42);
|
||||||
|
|
||||||
|
let z = MyEnum::Baz("hello", true);
|
||||||
|
|
||||||
|
x == MyEnum::Foo;
|
||||||
|
|
||||||
|
y != MyEnum::Bar(0);
|
||||||
|
|
||||||
|
// Detect enum types
|
||||||
|
|
||||||
|
x.type == "Foo";
|
||||||
|
|
||||||
|
y.type == "Bar";
|
||||||
|
|
||||||
|
z.type == "Baz";
|
||||||
|
|
||||||
|
// Extract enum fields
|
||||||
|
|
||||||
|
y.field_0 == 42;
|
||||||
|
|
||||||
|
y.field_1 == ();
|
||||||
|
|
||||||
|
z.field_0 == "hello";
|
||||||
|
|
||||||
|
z.field_1 == true;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Use `switch` Through Arrays
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Since enums are internally treated as [custom types], they are not _literals_ and cannot be
|
||||||
|
used as a match case in `switch` expressions. This is quite a limitation because the equivalent
|
||||||
|
`match` statement is commonly used in Rust to work with enums.
|
||||||
|
|
||||||
|
One way to work with Rust enums in a `switch` expression is through exposing the internal data
|
||||||
|
of each enum variant as an [array], usually with the name of the variant as the first item:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::Array;
|
||||||
|
|
||||||
|
engine.register_get("enum_data", |x: &mut Enum} {
|
||||||
|
match x {
|
||||||
|
Enum::Foo => vec!["Foo".into()] as Array,
|
||||||
|
Enum::Bar(value) => vec!["Bar".into(), (*value).into()] as Array,
|
||||||
|
Enum::Baz(val1, val2) => vec![
|
||||||
|
"Baz".into(), val1.clone().into(), (*val2).into()
|
||||||
|
] as Array
|
||||||
|
}
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Then it is a simple matter to match an enum via the `switch` expression:
|
Then it is a simple matter to match an enum via the `switch` expression:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// Assume 'value' = 'MyEnum::Baz("hello", true)'
|
// Assume 'value' = 'MyEnum::Baz("hello", true)'
|
||||||
// 'get_data' creates a variable-length array with 'MyEnum' data
|
// 'enum_data' creates a variable-length array with 'MyEnum' data
|
||||||
let x = switch value.enum_data {
|
let x = switch value.enum_data {
|
||||||
["Foo"] => 1,
|
["Foo"] => 1,
|
||||||
["Bar", 42] => 2,
|
["Bar", 42] => 2,
|
||||||
@ -61,4 +156,14 @@ let x = switch value.enum_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
x == 5;
|
x == 5;
|
||||||
|
|
||||||
|
// Which is essentially the same as:
|
||||||
|
let x = switch [value.type, value.field_0, value.field_1] {
|
||||||
|
["Foo", (), ()] => 1,
|
||||||
|
["Bar", 42, ()] => 2,
|
||||||
|
["Bar", 123, ()] => 3,
|
||||||
|
["Baz", "hello", false] => 4,
|
||||||
|
["Baz", "hello", true] => 5,
|
||||||
|
_ => 9
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
@ -123,6 +123,42 @@ x == 43;
|
|||||||
Notice that, when using a [module] as a [package], only functions registered at the _top level_
|
Notice that, when using a [module] as a [package], only functions registered at the _top level_
|
||||||
can be accessed. Variables as well as sub-modules are ignored.
|
can be accessed. Variables as well as sub-modules are ignored.
|
||||||
|
|
||||||
|
### Use `Engine::load_module`
|
||||||
|
|
||||||
|
Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro
|
||||||
|
to turn it into a normal Rhai [module], then use the `Engine::load_module` method on it:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
// The macro call creates a Rhai module from the plugin module.
|
||||||
|
let module = exported_module!(my_module);
|
||||||
|
|
||||||
|
// A module can simply be loaded as a globally-available module.
|
||||||
|
engine.load_module("service", module);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`),
|
||||||
|
plus the constant `MY_NUMBER`, are automatically loaded under the module namespace `service`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let x = service::greet("world");
|
||||||
|
x == "hello, world!";
|
||||||
|
|
||||||
|
service::MY_NUMBER == 42;
|
||||||
|
|
||||||
|
let x = service::greet(service::get_num().to_string());
|
||||||
|
x == "hello, 42!";
|
||||||
|
|
||||||
|
let x = service::get_num();
|
||||||
|
x == 42;
|
||||||
|
|
||||||
|
service::increment(x);
|
||||||
|
x == 43;
|
||||||
|
```
|
||||||
|
|
||||||
### Use as loadable `Module`
|
### Use as loadable `Module`
|
||||||
|
|
||||||
Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a
|
Using this directly as a dynamically-loadable Rhai [module] is almost the same, except that a
|
||||||
|
@ -25,7 +25,8 @@ Make the `Module` Available to the `Engine`
|
|||||||
`Engine::load_package` supports loading a [module] as a [package].
|
`Engine::load_package` supports loading a [module] as a [package].
|
||||||
|
|
||||||
Since it acts as a [package], all functions will be registered into the _global_ namespace
|
Since it acts as a [package], all functions will be registered into the _global_ namespace
|
||||||
and can be accessed without _module qualifiers_.
|
and can be accessed without _namespace qualifiers_. This is by far the easiest way to expose
|
||||||
|
a module's functionalities to Rhai.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Engine, Module};
|
use rhai::{Engine, Module};
|
||||||
@ -41,6 +42,25 @@ engine.eval::<i64>("inc(41)")? == 42; // no need to import module
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Make the `Module` a Global Module
|
||||||
|
------------------------------------
|
||||||
|
|
||||||
|
`Engine::load_module` loads a [module] and makes it available globally under a specific namespace.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, Module};
|
||||||
|
|
||||||
|
let mut module = Module::new(); // new module
|
||||||
|
module.set_fn_1("inc", |x: i64| Ok(x+1)); // use the 'set_fn_XXX' API to add functions
|
||||||
|
|
||||||
|
// Load the module into the Engine as a sub-module named 'calc'
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
engine.load_module("calc", module);
|
||||||
|
|
||||||
|
engine.eval::<i64>("calc::inc(41)")? == 42; // refer to the 'Calc' module
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Make the `Module` Dynamically Loadable
|
Make the `Module` Dynamically Loadable
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
|
@ -596,6 +596,8 @@ pub struct Engine {
|
|||||||
pub(crate) global_module: Module,
|
pub(crate) global_module: Module,
|
||||||
/// A collection of all library packages loaded into the Engine.
|
/// A collection of all library packages loaded into the Engine.
|
||||||
pub(crate) packages: PackagesCollection,
|
pub(crate) packages: PackagesCollection,
|
||||||
|
/// A collection of all sub-modules directly loaded into the Engine.
|
||||||
|
pub(crate) global_sub_modules: Imports,
|
||||||
|
|
||||||
/// A module resolution service.
|
/// A module resolution service.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -711,6 +713,7 @@ impl Engine {
|
|||||||
|
|
||||||
packages: Default::default(),
|
packages: Default::default(),
|
||||||
global_module: Default::default(),
|
global_module: Default::default(),
|
||||||
|
global_sub_modules: Default::default(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -773,6 +776,7 @@ impl Engine {
|
|||||||
|
|
||||||
packages: Default::default(),
|
packages: Default::default(),
|
||||||
global_module: Default::default(),
|
global_module: Default::default(),
|
||||||
|
global_sub_modules: Default::default(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
module_resolver: None,
|
module_resolver: None,
|
||||||
@ -1814,7 +1818,7 @@ impl Engine {
|
|||||||
|
|
||||||
Expr::True(_) => Ok(true.into()),
|
Expr::True(_) => Ok(true.into()),
|
||||||
Expr::False(_) => Ok(false.into()),
|
Expr::False(_) => Ok(false.into()),
|
||||||
Expr::Unit(_) => Ok(().into()),
|
Expr::Unit(_) => Ok(Dynamic::UNIT),
|
||||||
|
|
||||||
Expr::Custom(custom, _) => {
|
Expr::Custom(custom, _) => {
|
||||||
let expressions = custom
|
let expressions = custom
|
||||||
@ -2090,7 +2094,7 @@ impl Engine {
|
|||||||
} else if let Some(def_stmt) = def_stmt {
|
} else if let Some(def_stmt) = def_stmt {
|
||||||
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
self.eval_stmt(scope, mods, state, lib, this_ptr, def_stmt, level)
|
||||||
} else {
|
} else {
|
||||||
Ok(().into())
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,9 @@ use crate::fn_register::{RegisterFn, RegisterResultFn};
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec};
|
use crate::{fn_args::FuncArgs, fn_call::ensure_no_data_race, module::Module, StaticVec};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
use crate::fn_native::{shared_take_or_clone, Shared};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
|
|
||||||
@ -772,6 +775,45 @@ impl Engine {
|
|||||||
self.register_indexer_get(getter)
|
self.register_indexer_get(getter)
|
||||||
.register_indexer_set(setter)
|
.register_indexer_set(setter)
|
||||||
}
|
}
|
||||||
|
/// Register a `Module` as a sub-module with the `Engine`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
|
/// use rhai::{Engine, Module};
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// // Create the module
|
||||||
|
/// let mut module = Module::new();
|
||||||
|
/// module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||||
|
///
|
||||||
|
/// // Register the module as a sub-module
|
||||||
|
/// engine.register_module("CalcService", module);
|
||||||
|
///
|
||||||
|
/// assert_eq!(engine.eval::<i64>("CalcService::calc(41)")?, 42);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub fn register_module(
|
||||||
|
&mut self,
|
||||||
|
name: impl Into<ImmutableString>,
|
||||||
|
module: impl Into<Shared<Module>>,
|
||||||
|
) -> &mut Self {
|
||||||
|
let module = module.into();
|
||||||
|
|
||||||
|
if !module.is_indexed() {
|
||||||
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
|
let mut module = shared_take_or_clone(module);
|
||||||
|
module.build_index();
|
||||||
|
self.global_sub_modules.push(name, module);
|
||||||
|
} else {
|
||||||
|
self.global_sub_modules.push(name, module);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
/// Compile a string into an `AST`, which can be used later for evaluation.
|
/// Compile a string into an `AST`, which can be used later for evaluation.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -1368,7 +1410,8 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let mut mods = Default::default();
|
let mut mods = self.global_sub_modules.clone();
|
||||||
|
|
||||||
let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?;
|
let (result, _) = self.eval_ast_with_scope_raw(scope, &mut mods, ast)?;
|
||||||
|
|
||||||
let typ = self.map_type_name(result.type_name());
|
let typ = self.map_type_name(result.type_name());
|
||||||
@ -1446,7 +1489,8 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut mods = Default::default();
|
let mut mods = self.global_sub_modules.clone();
|
||||||
|
|
||||||
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()])
|
self.eval_statements_raw(scope, &mut mods, ast.statements(), &[ast.lib()])
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
@ -1599,7 +1643,7 @@ impl Engine {
|
|||||||
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
|
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), NO_POS))?;
|
||||||
|
|
||||||
let mut state = Default::default();
|
let mut state = Default::default();
|
||||||
let mut mods = Default::default();
|
let mut mods = self.global_sub_modules.clone();
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
if cfg!(not(feature = "no_closure")) {
|
if cfg!(not(feature = "no_closure")) {
|
||||||
|
@ -16,7 +16,11 @@ use crate::{calc_native_fn_hash, calc_script_fn_hash, StaticVec};
|
|||||||
use crate::ast::ScriptFnDef;
|
use crate::ast::ScriptFnDef;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::{ast::AST, engine::Engine, scope::Scope};
|
use crate::{
|
||||||
|
ast::AST,
|
||||||
|
engine::{Engine, Imports},
|
||||||
|
scope::Scope,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{Array, FN_IDX_GET, FN_IDX_SET};
|
||||||
@ -1361,7 +1365,8 @@ impl Module {
|
|||||||
ast: &AST,
|
ast: &AST,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
) -> Result<Self, Box<EvalAltResult>> {
|
) -> Result<Self, Box<EvalAltResult>> {
|
||||||
let mut mods = Default::default();
|
let mut mods = engine.global_sub_modules.clone();
|
||||||
|
let orig_mods_len = mods.len();
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
|
engine.eval_ast_with_scope_raw(&mut scope, &mut mods, &ast)?;
|
||||||
@ -1380,8 +1385,11 @@ impl Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Modules left in the scope become sub-modules
|
// Extra modules left in the scope become sub-modules
|
||||||
mods.iter().for_each(|(alias, m)| {
|
let mut func_mods: Imports = Default::default();
|
||||||
|
|
||||||
|
mods.into_iter().skip(orig_mods_len).for_each(|(alias, m)| {
|
||||||
|
func_mods.push(alias.clone(), m.clone());
|
||||||
module.set_sub_module(alias, m);
|
module.set_sub_module(alias, m);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1396,7 +1404,7 @@ impl Module {
|
|||||||
// Encapsulate AST environment
|
// Encapsulate AST environment
|
||||||
let mut func = func.as_ref().clone();
|
let mut func = func.as_ref().clone();
|
||||||
func.lib = Some(ast_lib.clone());
|
func.lib = Some(ast_lib.clone());
|
||||||
func.mods = mods.clone();
|
func.mods = func_mods.clone();
|
||||||
module.set_script_fn(func.into());
|
module.set_script_fn(func.into());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user