diff --git a/src/build/compile_test.rs b/src/build/compile_test.rs index c59569f..468110e 100644 --- a/src/build/compile_test.rs +++ b/src/build/compile_test.rs @@ -19,8 +19,9 @@ use super::assets::MemoryCache; use super::Builder; fn assert_build(input: &str) { + let i_paths = Vec::new(); let cache = MemoryCache::new(); - let mut b = Builder::new("", Rc::new(RefCell::new(cache))); + let mut b = Builder::new("", &i_paths, Rc::new(RefCell::new(cache))); b.enable_validate_mode(); b.eval_string(input).unwrap(); if !b.assert_collector.success { diff --git a/src/build/mod.rs b/src/build/mod.rs index 0cd5405..6d7c673 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -93,9 +93,9 @@ pub struct AssertCollector { } /// Builder handles building ucg code for a single file. -pub struct Builder { +pub struct Builder<'a> { file: PathBuf, - import_path: Vec, + import_path: &'a Vec, validate_mode: bool, pub assert_collector: AssertCollector, strict: bool, @@ -139,24 +139,36 @@ macro_rules! eval_binary_expr { }; } -impl Builder { +impl<'a> Builder<'a> { /// Constructs a new Builder. - pub fn new>(file: P, cache: Rc>) -> Self { - Self::new_with_scope(file, cache, HashMap::new()) + pub fn new>( + file: P, + import_paths: &'a Vec, + cache: Rc>, + ) -> Self { + Self::new_with_scope(file, import_paths, cache, HashMap::new()) } /// Constructs a new Builder with a provided scope. pub fn new_with_scope>( root: P, + import_paths: &'a Vec, cache: Rc>, scope: ValueMap, ) -> Self { let env_vars: Vec<(String, String)> = env::vars().collect(); - Self::new_with_env_and_scope(root, cache, scope, Rc::new(Val::Env(env_vars))) + Self::new_with_env_and_scope( + root, + import_paths, + cache, + scope, + Rc::new(Val::Env(env_vars)), + ) } pub fn new_with_env_and_scope>( file: P, + import_paths: &'a Vec, cache: Rc>, scope: ValueMap, env: Rc, @@ -166,7 +178,7 @@ impl Builder { // Our import stack is initialized with ourself. import_stack: vec![file.to_string_lossy().to_string()], file: file, - import_path: Vec::new(), + import_path: import_paths, validate_mode: false, assert_collector: AssertCollector { success: true, @@ -189,7 +201,7 @@ impl Builder { // Our import stack is initialized with ourself. import_stack: self.import_stack.clone(), file: file.into(), - import_path: self.import_path.clone(), + import_path: self.import_path, validate_mode: false, assert_collector: AssertCollector { success: true, @@ -215,10 +227,6 @@ impl Builder { self.strict = to; } - pub fn set_import_paths(&mut self, paths: Vec) { - self.import_path = paths; - } - pub fn prepend_import_stack(&mut self, imports: &Vec) { let mut new_stack = self.import_stack.clone(); new_stack.append(imports.clone().as_mut()); diff --git a/src/build/test.rs b/src/build/test.rs index 389f0e4..d1b61ad 100644 --- a/src/build/test.rs +++ b/src/build/test.rs @@ -28,8 +28,9 @@ fn test_expr_to_val(mut cases: Vec<(Expression, Val)>, mut b: Builder) { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_div_expr_fail() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let b = Builder::new(std::env::current_dir().unwrap(), cache); + let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -53,8 +54,9 @@ fn test_eval_div_expr_fail() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_mul_expr_fail() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let b = Builder::new(std::env::current_dir().unwrap(), cache); + let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -78,8 +80,9 @@ fn test_eval_mul_expr_fail() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_subtract_expr_fail() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let b = Builder::new(std::env::current_dir().unwrap(), cache); + let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -102,8 +105,9 @@ fn test_eval_subtract_expr_fail() { #[test] #[should_panic(expected = "Expected Float")] fn test_eval_add_expr_fail() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let b = Builder::new(std::env::current_dir().unwrap(), cache); + let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); test_expr_to_val( vec![( Expression::Binary(BinaryOpDef { @@ -126,8 +130,9 @@ fn test_eval_add_expr_fail() { #[test] fn test_eval_simple_lookup_error() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); b.build_output .entry(value_node!("var1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(1))); @@ -142,8 +147,9 @@ fn test_eval_simple_lookup_error() { #[test] #[should_panic(expected = "Unable to find binding tpl1")] fn test_expr_copy_no_such_tuple() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let b = Builder::new(std::env::current_dir().unwrap(), cache); + let b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); test_expr_to_val( vec![( Expression::Copy(CopyDef { @@ -163,8 +169,9 @@ fn test_expr_copy_no_such_tuple() { #[test] #[should_panic(expected = "Expected Tuple or Module got Int(1)")] fn test_expr_copy_not_a_tuple() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); b.build_output .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(1))); @@ -187,8 +194,9 @@ fn test_expr_copy_not_a_tuple() { #[test] #[should_panic(expected = "Expected type Integer for field fld1 but got String")] fn test_expr_copy_field_type_error() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); b.build_output .entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Tuple(vec![( @@ -223,8 +231,9 @@ fn test_expr_copy_field_type_error() { #[test] #[should_panic(expected = "Unable to find binding arg1")] fn test_macro_hermetic() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); b.build_output .entry(value_node!("arg1".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Str("bar".to_string()))); @@ -266,8 +275,9 @@ fn test_macro_hermetic() { #[test] #[should_panic(expected = "Expected String but got Integer in Select expression")] fn test_select_expr_not_a_string() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); b.build_output .entry(value_node!("foo".to_string(), Position::new(1, 0, 0))) .or_insert(Rc::new(Val::Int(4))); diff --git a/src/convert/exec.rs b/src/convert/exec.rs index 484ab01..547a167 100644 --- a/src/convert/exec.rs +++ b/src/convert/exec.rs @@ -205,8 +205,9 @@ mod exec_test { #[test] fn convert_just_command_test() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -226,8 +227,9 @@ mod exec_test { #[test] fn convert_command_with_env_test() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); let conv = ExecConverter::new(); b.eval_string( "let script = { @@ -254,8 +256,9 @@ mod exec_test { #[test] fn convert_command_with_arg_test() { + let i_paths = Vec::new(); let cache = Rc::new(RefCell::new(MemoryCache::new())); - let mut b = Builder::new(std::env::current_dir().unwrap(), cache); + let mut b = Builder::new(std::env::current_dir().unwrap(), &i_paths, cache); let conv = ExecConverter::new(); b.eval_string( "let script = { diff --git a/src/main.rs b/src/main.rs index f1dd6ed..c0bef20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,17 +72,18 @@ fn run_converter(c: &traits::Converter, v: Rc, f: Option<&str>) -> traits:: c.convert(v, file.as_mut()) } -fn build_file( - file: &str, +fn build_file<'a>( + file: &'a str, validate: bool, strict: bool, + import_paths: &'a Vec, cache: Rc>, -) -> Result> { +) -> Result, Box> { let mut file_path_buf = PathBuf::from(file); if file_path_buf.is_relative() { file_path_buf = std::env::current_dir().unwrap().join(file_path_buf); } - let mut builder = build::Builder::new(file_path_buf, cache); + let mut builder = build::Builder::new(file_path_buf, import_paths, cache); builder.set_strict(strict); if validate { builder.enable_validate_mode(); @@ -94,9 +95,14 @@ fn build_file( Ok(builder) } -fn do_validate(file: &str, strict: bool, cache: Rc>) -> bool { +fn do_validate( + file: &str, + strict: bool, + import_paths: &Vec, + cache: Rc>, +) -> bool { println!("Validating {}", file); - match build_file(file, true, strict, cache) { + match build_file(file, true, strict, import_paths, cache) { Ok(b) => { if b.assert_collector.success { println!("File {} Pass\n", file); @@ -116,11 +122,12 @@ fn do_validate(file: &str, strict: bool, cache: Rc>) -> bool { fn do_compile( file: &str, strict: bool, + import_paths: &Vec, cache: Rc>, registry: &ConverterRegistry, ) -> bool { println!("Building {}", file); - let builder = match build_file(file, false, strict, cache.clone()) { + let builder = match build_file(file, false, strict, import_paths, cache.clone()) { Ok(builder) => builder, Err(err) => { eprintln!("{}", err); @@ -152,6 +159,7 @@ fn visit_ucg_files( recurse: bool, validate: bool, strict: bool, + import_paths: &Vec, cache: Rc>, registry: &ConverterRegistry, ) -> Result> { @@ -177,6 +185,7 @@ fn visit_ucg_files( recurse, validate, strict, + import_paths, cache.clone(), registry, ) { @@ -185,28 +194,34 @@ fn visit_ucg_files( } } else { if validate && path_as_string.ends_with("_test.ucg") { - if !do_validate(&path_as_string, strict, cache.clone()) { + if !do_validate(&path_as_string, strict, import_paths, cache.clone()) { result = false; summary.push_str(format!("{} - FAIL\n", path_as_string).as_str()) } else { summary.push_str(format!("{} - PASS\n", path_as_string).as_str()) } } else if !validate && path_as_string.ends_with(".ucg") { - if !do_compile(&path_as_string, strict, cache.clone(), registry) { + if !do_compile( + &path_as_string, + strict, + import_paths, + cache.clone(), + registry, + ) { result = false; } } } } } else if validate && our_path.ends_with("_test.ucg") { - if !do_validate(&our_path, strict, cache) { + if !do_validate(&our_path, strict, import_paths, cache) { result = false; summary.push_str(format!("{} - FAIL\n", our_path).as_str()); } else { summary.push_str(format!("{} - PASS\n", &our_path).as_str()); } } else if !validate { - if !do_compile(&our_path, strict, cache, registry) { + if !do_compile(&our_path, strict, import_paths, cache, registry) { result = false; } } @@ -219,6 +234,7 @@ fn visit_ucg_files( fn inspect_command( matches: &clap::ArgMatches, + import_paths: &Vec, cache: Rc>, registry: &ConverterRegistry, strict: bool, @@ -226,7 +242,7 @@ fn inspect_command( let file = matches.value_of("INPUT").unwrap(); let sym = matches.value_of("sym"); let target = matches.value_of("target").unwrap(); - let mut builder = build::Builder::new(file, cache); + let mut builder = build::Builder::new(file, import_paths, cache); builder.set_strict(strict); match registry.get_converter(target) { Some(converter) => { @@ -262,6 +278,7 @@ fn inspect_command( fn build_command( matches: &clap::ArgMatches, + import_paths: &Vec, cache: Rc>, registry: &ConverterRegistry, strict: bool, @@ -276,6 +293,7 @@ fn build_command( recurse, false, strict, + import_paths, cache.clone(), ®istry, ); @@ -286,7 +304,15 @@ fn build_command( } for file in files.unwrap() { let pb = PathBuf::from(file); - if let Ok(false) = visit_ucg_files(&pb, recurse, false, strict, cache.clone(), ®istry) { + if let Ok(false) = visit_ucg_files( + &pb, + recurse, + false, + strict, + import_paths, + cache.clone(), + ®istry, + ) { ok = false; } } @@ -297,6 +323,7 @@ fn build_command( fn test_command( matches: &clap::ArgMatches, + import_paths: &Vec, cache: Rc>, registry: &ConverterRegistry, strict: bool, @@ -310,6 +337,7 @@ fn test_command( recurse, true, strict, + import_paths, cache.clone(), ®istry, ); @@ -326,6 +354,7 @@ fn test_command( recurse, true, strict, + import_paths, cache.clone(), ®istry, ) { @@ -355,17 +384,23 @@ fn main() { let app_matches = app.clone().get_matches(); let cache: Rc> = Rc::new(RefCell::new(MemoryCache::new())); let registry = ConverterRegistry::make_registry(); + let mut import_paths = Vec::new(); + if let Ok(path_list_str) = std::env::var("UCG_IMPORT_PATH") { + for p in std::env::split_paths(&path_list_str) { + import_paths.push(p); + } + } let strict = if app_matches.is_present("nostrict") { false } else { true }; if let Some(matches) = app_matches.subcommand_matches("inspect") { - inspect_command(matches, cache, ®istry, strict); + inspect_command(matches, &import_paths, cache, ®istry, strict); } else if let Some(matches) = app_matches.subcommand_matches("build") { - build_command(matches, cache, ®istry, strict); + build_command(matches, &import_paths, cache, ®istry, strict); } else if let Some(matches) = app_matches.subcommand_matches("test") { - test_command(matches, cache, ®istry, strict); + test_command(matches, &import_paths, cache, ®istry, strict); } else if let Some(_) = app_matches.subcommand_matches("converters") { converters_command(®istry) } else {