diff --git a/src/build/ir.rs b/src/build/ir.rs index f466796..4990316 100644 --- a/src/build/ir.rs +++ b/src/build/ir.rs @@ -19,6 +19,7 @@ pub enum Val { Str(String), List(Vec>), Tuple(Vec<(PositionedItem, Rc)>), + Env(Vec<(String, String)>), Macro(MacroDef), Module(ModuleDef), } @@ -34,6 +35,7 @@ impl Val { &Val::Str(_) => "String".to_string(), &Val::List(_) => "List".to_string(), &Val::Tuple(_) => "Tuple".to_string(), + &Val::Env(_) => "Env".to_string(), &Val::Macro(_) => "Macro".to_string(), &Val::Module(_) => "Module".to_string(), } @@ -51,6 +53,7 @@ impl Val { &Val::Str(_), &Val::List(_), &Val::Tuple(_), + &Val::Env(_), &Val::Macro(_), &Val::Module(_) ) @@ -165,6 +168,13 @@ impl Val { return false; } + pub fn is_env(&self) -> bool { + if let &Val::Env(_) = self { + return true; + } + return false; + } + pub fn is_list(&self) -> bool { if let &Val::List(_) = self { return true; @@ -204,6 +214,13 @@ impl Display for Val { } write!(f, ")") } + &Val::Env(ref def) => { + try!(write!(f, "Env(\n")); + for v in def.iter() { + try!(write!(f, "\t{}=\"{}\"\n", v.0, v.1)); + } + write!(f, ")") + } } } } diff --git a/src/build/mod.rs b/src/build/mod.rs index 8be105b..da428de 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -98,6 +98,7 @@ pub struct Builder<'a> { curr_file: Option<&'a str>, validate_mode: bool, pub assert_collector: AssertCollector, + strict: bool, env: Rc, // NOTE(jwall): We use interior mutability here because we need // our asset cache to be shared by multiple different sub-builders. @@ -140,6 +141,51 @@ macro_rules! eval_binary_expr { } impl<'a> Builder<'a> { + /// Constructs a new Builder. + pub fn new>(root: P, cache: Rc>) -> Self { + Self::new_with_scope(root, cache, HashMap::new()) + } + + /// Constructs a new Builder with a provided scope. + pub fn new_with_scope>( + root: P, + 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))) + } + + pub fn new_with_env_and_scope>( + root: P, + cache: Rc>, + scope: ValueMap, + env: Rc, + ) -> Self { + Builder { + file: root.into(), + curr_file: None, + validate_mode: false, + assert_collector: AssertCollector { + success: true, + summary: String::new(), + failures: String::new(), + }, + env: env, + strict: true, + assets: cache, + build_output: scope, + out_lock: None, + stack: None, + is_module: false, + last: None, + } + } + + pub fn set_strict(&mut self, to: bool) { + self.strict = to; + } + // TOOD(jwall): This needs some unit tests. fn tuple_to_val(&mut self, fields: &Vec<(Token, Expression)>) -> Result, Box> { let mut new_fields = Vec::<(PositionedItem, Rc)>::new(); @@ -185,52 +231,6 @@ impl<'a> Builder<'a> { } } - /// Constructs a new Builder. - pub fn new>(root: P, cache: Rc>) -> Self { - Self::new_with_scope(root, cache, HashMap::new()) - } - - /// Constructs a new Builder with a provided scope. - pub fn new_with_scope>( - root: P, - cache: Rc>, - scope: ValueMap, - ) -> Self { - let env_vars: Vec<(PositionedItem, Rc)> = env::vars() - .map(|t| { - ( - PositionedItem::new(t.0, Position::new(0, 0, 0)), - Rc::new(t.1.into()), - ) - }).collect(); - Self::new_with_env_and_scope(root, cache, scope, Rc::new(Val::Tuple(env_vars))) - } - - pub fn new_with_env_and_scope>( - root: P, - cache: Rc>, - scope: ValueMap, - env: Rc, - ) -> Self { - Builder { - file: root.into(), - curr_file: None, - validate_mode: false, - assert_collector: AssertCollector { - success: true, - summary: String::new(), - failures: String::new(), - }, - env: env, - assets: cache, - build_output: scope, - out_lock: None, - stack: None, - is_module: false, - last: None, - } - } - /// Returns a Val by name from previously built UCG. pub fn get_out_by_name(&self, name: &str) -> Option> { let key = PositionedItem { @@ -442,6 +442,32 @@ impl<'a> Builder<'a> { return None; } + fn lookup_in_env( + &self, + search: &Token, + stack: &mut VecDeque>, + fs: &Vec<(String, String)>, + ) -> Result<(), Box> { + for &(ref name, ref val) in fs.iter() { + if &search.fragment == name { + stack.push_back(Rc::new(Val::Str(val.clone()))); + return Ok(()); + } else if !self.strict { + eprintln!( + "Environment Variable {} not set using NULL instead.", + search.fragment + ); + stack.push_back(Rc::new(Val::Empty)); + return Ok(()); + } + } + return Err(Box::new(error::BuildError::new( + format!("Environment Variable {} not set", search.fragment), + error::ErrorType::NoSuchSymbol, + search.pos.clone(), + ))); + } + fn lookup_in_tuple( &self, stack: &mut VecDeque>, @@ -506,6 +532,9 @@ impl<'a> Builder<'a> { &Val::List(_) => { stack.push_back(first.clone()); } + &Val::Env(_) => { + stack.push_back(first.clone()); + } _ => { // noop } @@ -529,6 +558,10 @@ impl<'a> Builder<'a> { try!(self.lookup_in_tuple(&mut stack, sl, (&next.pos, &next.fragment), fs)); continue; } + &Val::Env(ref fs) => { + try!(self.lookup_in_env(&next, &mut stack, fs)); + continue; + } &Val::List(ref elems) => { try!(self.lookup_in_list( &mut stack, diff --git a/src/convert/env.rs b/src/convert/env.rs index a02fe86..849fbe5 100644 --- a/src/convert/env.rs +++ b/src/convert/env.rs @@ -82,6 +82,10 @@ impl EnvConverter { // This is ignored eprintln!("Skipping macro..."); } + &Val::Env(ref _fs) => { + // This is ignored + eprintln!("Skipping env..."); + } &Val::Module(ref _def) => { // This is ignored eprintln!("Skipping module..."); diff --git a/src/convert/flags.rs b/src/convert/flags.rs index 23cec1b..2602532 100644 --- a/src/convert/flags.rs +++ b/src/convert/flags.rs @@ -98,6 +98,10 @@ impl FlagConverter { // This is ignored eprintln!("Skipping macro..."); } + &Val::Env(ref _fs) => { + // This is ignored + eprintln!("Skipping env..."); + } &Val::Module(ref _def) => { // This is ignored eprintln!("Skipping module..."); diff --git a/src/convert/json.rs b/src/convert/json.rs index 2d9b8c0..f407a5a 100644 --- a/src/convert/json.rs +++ b/src/convert/json.rs @@ -46,6 +46,15 @@ impl JsonConverter { Ok(serde_json::Value::Object(mp)) } + fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result { + let mut mp = serde_json::Map::new(); + for &(ref k, ref v) in items.iter() { + mp.entry(k.clone()) + .or_insert(serde_json::Value::String(v.clone())); + } + Ok(serde_json::Value::Object(mp)) + } + fn convert_value(&self, v: &Val) -> std::io::Result { let jsn_val = match v { &Val::Boolean(b) => serde_json::Value::Bool(b), @@ -75,6 +84,7 @@ impl JsonConverter { eprintln!("Skipping module encoding as null..."); serde_json::Value::Null } + &Val::Env(ref fs) => try!(self.convert_env(fs)), &Val::List(ref l) => try!(self.convert_list(l)), &Val::Tuple(ref t) => try!(self.convert_tuple(t)), }; diff --git a/src/convert/toml.rs b/src/convert/toml.rs index 05dd0ad..7d9bff0 100644 --- a/src/convert/toml.rs +++ b/src/convert/toml.rs @@ -50,6 +50,15 @@ impl TomlConverter { Ok(toml::Value::Table(mp)) } + fn convert_env(&self, items: &Vec<(String, String)>) -> ConvertResult { + let mut mp = toml::value::Table::new(); + for &(ref k, ref v) in items.iter() { + mp.entry(k.clone()) + .or_insert(toml::Value::String(v.clone())); + } + Ok(toml::Value::Table(mp)) + } + fn convert_value(&self, v: &Val) -> ConvertResult { let toml_val = match v { &Val::Boolean(b) => toml::Value::Boolean(b), @@ -69,6 +78,7 @@ impl TomlConverter { let err = SimpleError::new("Modules are not allowed in Toml Conversions!"); return Err(Box::new(err)); } + &Val::Env(ref fs) => try!(self.convert_env(fs)), &Val::List(ref l) => try!(self.convert_list(l)), &Val::Tuple(ref t) => try!(self.convert_tuple(t)), }; diff --git a/src/convert/yaml.rs b/src/convert/yaml.rs index 3a1e4ca..135982b 100644 --- a/src/convert/yaml.rs +++ b/src/convert/yaml.rs @@ -23,6 +23,17 @@ impl YamlConverter { Ok(serde_yaml::Value::Sequence(v)) } + fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result { + let mut mp = serde_yaml::Mapping::new(); + for &(ref k, ref v) in items.iter() { + mp.insert( + serde_yaml::Value::String(k.clone()), + serde_yaml::Value::String(v.clone()), + ); + } + Ok(serde_yaml::Value::Mapping(mp)) + } + fn convert_tuple( &self, items: &Vec<(ast::PositionedItem, Rc)>, @@ -58,6 +69,7 @@ impl YamlConverter { eprintln!("Skipping module encoding as null..."); serde_yaml::Value::Null } + &Val::Env(ref fs) => try!(self.convert_env(fs)), &Val::List(ref l) => try!(self.convert_list(l)), &Val::Tuple(ref t) => try!(self.convert_tuple(t)), };