1use std::path::Path;
2use std::sync::Arc;
3
4use miette::Result;
5use rhai::module_resolvers::FileModuleResolver;
6use rhai::Engine;
7use rhai::Module;
8use rhai::Scope;
9use rhai::Variant;
10
11use crate::yolk::EvalMode;
12
13use super::rhai_error::RhaiError;
14use super::stdlib;
15
16pub const YOLK_TEXT_NAME: &str = "YOLK_TEXT";
17
18#[derive(Debug)]
19pub struct EvalCtx {
20 engine: Engine,
21 scope: Scope<'static>,
22 yolk_file_module: Option<(rhai::AST, Arc<Module>)>,
23}
24
25impl Default for EvalCtx {
26 fn default() -> Self {
27 Self::new_empty()
28 }
29}
30
31impl EvalCtx {
32 pub fn new_empty() -> Self {
33 let mut engine = Engine::new();
34 engine.set_optimization_level(rhai::OptimizationLevel::Simple);
35
36 engine.build_type::<super::sysinfo::SystemInfo>();
37 engine.build_type::<super::sysinfo::SystemInfoPaths>();
38 Self {
39 engine,
40 scope: Scope::new(),
41 yolk_file_module: None,
42 }
43 }
44
45 pub fn new_in_mode(mode: EvalMode) -> Result<Self> {
50 let mut ctx = Self::new_empty();
51 ctx.engine
52 .register_global_module(Arc::new(stdlib::global_stuff()));
53 ctx.engine
54 .register_static_module("utils", Arc::new(stdlib::utils_module()));
55 ctx.engine
56 .register_static_module("io", Arc::new(stdlib::io_module(mode)));
57 let template_module = Arc::new(stdlib::tag_module());
58 ctx.engine
59 .register_static_module("template", template_module);
60
61 Ok(ctx)
62 }
63
64 pub fn set_module_path(&mut self, path: &Path) {
69 self.engine
70 .set_module_resolver(FileModuleResolver::new_with_path(path));
71 }
72
73 pub fn load_rhai_file_to_module(&mut self, content: &str) -> Result<(), RhaiError> {
75 let ast = self.compile(content)?;
76 let module = Module::eval_ast_as_new(self.scope.clone(), &ast, &self.engine)
77 .map_err(|e| RhaiError::from_rhai(content, *e))?;
78 let module = Arc::new(module);
79 self.engine.register_global_module(module.clone());
80 self.yolk_file_module = Some((ast, module.clone()));
81 Ok(())
82 }
83
84 pub fn eval_rhai<T: Variant + Clone>(&mut self, content: &str) -> Result<T, RhaiError> {
86 let mut ast = self.compile(content)?;
87 if let Some((yolk_file_ast, _)) = self.yolk_file_module.as_ref() {
88 ast = yolk_file_ast.merge(&ast);
89 }
90 self.engine
91 .eval_ast_with_scope(&mut self.scope, &ast)
92 .map_err(|e| RhaiError::from_rhai(content, *e))
93 }
94
95 pub fn eval_text_transformation(
98 &mut self,
99 text: &str,
100 expr: &str,
101 ) -> Result<String, RhaiError> {
102 let scope_before = self.scope.len();
103 let text = text.to_string();
104 self.engine
105 .register_fn("get_yolk_text", move || text.clone());
106 let result = self.eval_rhai::<String>(expr)?;
107 self.scope.rewind(scope_before);
108 Ok(result.to_string())
109 }
110
111 pub fn set_global<T: Variant + Clone>(&mut self, name: &str, value: T) {
112 self.scope.set_or_push(name, value);
113 }
114
115 pub fn engine_mut(&mut self) -> &mut Engine {
116 &mut self.engine
117 }
118
119 pub fn yolk_file_module(&self) -> Option<&(rhai::AST, Arc<Module>)> {
120 self.yolk_file_module.as_ref()
121 }
122
123 fn compile(&mut self, text: &str) -> Result<rhai::AST, RhaiError> {
124 self.engine
125 .compile(text)
126 .map_err(|e| RhaiError::from_rhai_compile(text, e))
127 }
128}