diff --git a/doc/src/start/examples/scripts.md b/doc/src/start/examples/scripts.md index d748d8a0..f3167a93 100644 --- a/doc/src/start/examples/scripts.md +++ b/doc/src/start/examples/scripts.md @@ -20,6 +20,7 @@ There are also a number of examples scripts that showcase Rhai's features, all i | [`function_decl3.rhai`]({{repoTree}}/scripts/function_decl3.rhai) | a [function] with many parameters | | [`if1.rhai`]({{repoTree}}/scripts/if1.rhai) | [`if`]({{rootUrl}}/language/if.md) example | | [`loop.rhai`]({{repoTree}}/scripts/loop.rhai) | count-down [`loop`]({{rootUrl}}/language/loop.md) in Rhai, emulating a `do` .. `while` loop | +| [`module.rhai`]({{repoTree}}/scripts/module.rhai) | import a script file as a module | | [`oop.rhai`]({{repoTree}}/scripts/oop.rhai) | simulate [object-oriented programming (OOP)][OOP] with [closures] | | [`op1.rhai`]({{repoTree}}/scripts/op1.rhai) | just simple addition | | [`op2.rhai`]({{repoTree}}/scripts/op2.rhai) | simple addition and multiplication | diff --git a/scripts/module.rhai b/scripts/module.rhai new file mode 100644 index 00000000..0ad387c1 --- /dev/null +++ b/scripts/module.rhai @@ -0,0 +1,3 @@ +import "scripts/loop"; + +print("Module test!"); diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 77756247..a76f26d0 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -4,7 +4,9 @@ use crate::module::{Module, ModuleResolver}; use crate::result::EvalAltResult; use crate::token::Position; -use crate::stdlib::{boxed::Box, collections::HashMap, path::PathBuf, string::String}; +use crate::stdlib::{ + boxed::Box, collections::HashMap, io::Error as IoError, path::PathBuf, string::String, +}; /// Module resolution service that loads module script files from the file system. /// @@ -135,34 +137,42 @@ impl ModuleResolver for FileModuleResolver { let scope = Default::default(); // See if it is cached - let mut module = None; + let mut module: Option> = None; - let module_ref = { + let mut module_ref = { #[cfg(not(feature = "sync"))] let c = self.cache.borrow(); #[cfg(feature = "sync")] let c = self.cache.read().unwrap(); if let Some(module) = c.get(&file_path) { - module.clone() + Some(module.clone()) } else { - // Load the file and compile it if not found - let ast = engine.compile_file(file_path.clone()).map_err(|err| { - Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) - })?; - - let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| { - Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) - })?; - - m.build_index(); - - let m: Shared = m.into(); - module = Some(m.clone()); - m + None } }; + if module_ref.is_none() { + // Load the script file and compile it + let ast = engine + .compile_file(file_path.clone()) + .map_err(|err| match *err { + EvalAltResult::ErrorSystem(_, err) if err.is::() => { + Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) + } + _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), + })?; + + let mut m = Module::eval_ast_as_new(scope, &ast, engine).map_err(|err| { + Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)) + })?; + + m.build_index(); + + module = Some(m.into()); + module_ref = module.clone(); + }; + if let Some(module) = module { // Put it into the cache #[cfg(not(feature = "sync"))] @@ -171,6 +181,6 @@ impl ModuleResolver for FileModuleResolver { self.cache.write().unwrap().insert(file_path, module); } - Ok(module_ref) + Ok(module_ref.unwrap()) } } diff --git a/tests/modules.rs b/tests/modules.rs index ab51f4e4..97496881 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -432,3 +432,16 @@ fn test_module_ast_namespace2() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_module_file() -> Result<(), Box> { + let engine = Engine::new(); + let ast = engine.compile( + r#" + import "scripts/module"; + print("top"); + "#, + )?; + Module::eval_ast_as_new(Default::default(), &ast, &engine)?; + Ok(()) +}