Dynamically importing a module and calling it's functions #1024
-
Hello! Recently, in #1021 with the help of @schungx I managed to create somewhat complex parser to parse JSX-like expressions with Rhai: fn template(assets, content, props) {
assets.assets.add("resouces/controller_foo.tsx");
component {
<!DOCTYPE html>
<LayoutHomepage extraBodyClass="my-extra-class">
< button
class="myclass"
data-foo={props.bar}
data-fooz={`${props.bar}`}
data-gooz={if true {
component {
<div />
}
} else {
":)"
}}
disabled
>
<b><i><u>test</u></i></b>
Hello! :D
{" - "}
<Note>
{if content.is_empty() {
component {
<div>
NOTE EMPTY CONTENT
</div>
}
} else {
content
}}
</Note>
</button>
</LayoutHomepage>
}
}
template(#{
render: || "wow",
assets: #{
add: |script| script,
}
}, "", #{
bar: "baz tag \" attribute"
}) I am past the parser, the above more or less works, but I have a few questions now:
is it possible to import a module dynamically with I know it might sound silly, but now that I have the components like I tried a few things; using What I generally want to achieve is to this: fn template(content) {
component {
<MyOtherComponent>
<b>{content}</b>
</MyOtherComponent>
}
} to work exactly like this, at a custom syntax level (parsing already works, now I am at the evaluation stage): // import added automatically
import "MyOtherComponent" as MyOtherComponent;
fn template(content) {
MyOtherComponent::template(`<b>${content}</b>`)
} Any tips? : D Thank you The parser in question (runs as a single file), I am implementing example code
|
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 17 replies
-
I'll have to look into that. Would you give an example?
What you need is a way to Now if you'd like to call a function from that module, push it into the Since you said you've already tried it, can you let me know how it didn't work? Normally, we'd prep up the |
Beta Was this translation helpful? Give feedback.
-
Yeah, example is in that file I pasted. :) I know it's long but it runs standalone without modifications. If you set up a new project with just that file, add Rhai to cargo, uncomment strict variables, then run
Ok, prepping the engine can also work, because I can just scan the directory with "components" upfront and add each of them as a module. It doesn't need to be dynamic, I am just looking for any way to get this to work. :D I couldn't figure that out though; I tried importing a module like "LayoutHomepage", then calling "LayoutHomepage::template" function, but that didn't work. I got a message that a function does not exist. Is there some specific syntax somewhere for calling functions from specific modules? The example from the docs that explains how to call functions in Rust from script or AST works, but that is not exactly this case, and I can't find examples on getting it to work with imported modules either. If you could comment on some of my points above that would help a lot also. In the meantime, I will try to push a module function onto a global state and pop it as you say, then come back. Thanks for that tip. Edit: I tried pushing the module onto
Also, my other issue is - Is there a way to call a function within the context of a Rhai script? I want it to be able to access local variables from Rhai function (positional arguments), without transferring them to Rust and back. Edit 2: if I import static modules: engine.register_static_module("Note", note_module); Then this works: println!("Eval result: {:#?}", context.engine().eval::<Dynamic>("Note::template(1, 2, 3)")?); This doesn't ( context.call_fn("Note::template", (
Dynamic::from(""),
Dynamic::from(""),
Dynamic::from(""),
))?; |
Beta Was this translation helpful? Give feedback.
-
Ok, I finally managed to figure it out. Thanks a lot for the help @schungx ! I've added the meta module builder: use std::sync::Arc;
use anyhow::Result;
use rhai::Engine;
use rhai::Module;
use rhai::Scope;
use super::component_reference::ComponentReference;
use super::component_registry::ComponentRegsitry;
pub struct ComponentMetaModule {
component_registry: Arc<ComponentRegsitry>,
}
impl ComponentMetaModule {
pub fn into_global_module(self, engine: &Engine) -> Result<Module> {
let mut meta_script = String::new();
for entry in &self.component_registry.components {
let ComponentReference {
global_fn_name,
name,
path,
} = entry.value();
meta_script.push_str(&format!(
r#"
import "{path}" as {name};
fn {global_fn_name}(context, props, content) {{
{name}::template(context, props, content)
}}
"#
));
}
let meta_module_ast = engine.compile(meta_script)?;
Ok(Module::eval_ast_as_new(
Scope::new(),
&meta_module_ast,
&engine,
)?)
}
}
impl From<Arc<ComponentRegsitry>> for ComponentMetaModule {
fn from(component_registry: Arc<ComponentRegsitry>) -> Self {
Self { component_registry }
}
} Then I register it in the engine: let meta_module = ComponentMetaModule::from(component_registry.clone()).into_global_module(&engine)?;
engine.register_global_module(meta_module.into()); Then, later, during evaluation I can do: Ok(eval_context
.call_fn::<Dynamic>(
component_registry
.get_global_fn_name(&opening_tag.name)
.or_else(|err| {
Err(EvalAltResult::ErrorRuntime(
format!("Component not found: {err}").into(),
rhai::Position::NONE,
))
})?,
(
Dynamic::from(component_context.clone()),
Dynamic::from_map(props),
Dynamic::from(result.clone()),
),
)
.or_else(|err| {
Err(EvalAltResult::ErrorRuntime(
format!("Failed to call component function: {err}").into(),
rhai::Position::NONE,
))
})?) I think this is what I was missing. : D Finally works, thanks again |
Beta Was this translation helpful? Give feedback.
Ok, I finally managed to figure it out. Thanks a lot for the help @schungx !
I've added the meta module builder: