FEATURE: support a strict mode for building.

When strict mode is off then give a warning for unset env variables
and return NULL.
This commit is contained in:
Jeremy Wall 2018-11-26 21:36:50 -06:00
parent 1365a38700
commit aa183960d3
7 changed files with 136 additions and 46 deletions

View File

@ -19,6 +19,7 @@ pub enum Val {
Str(String),
List(Vec<Rc<Val>>),
Tuple(Vec<(PositionedItem<String>, Rc<Val>)>),
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, ")")
}
}
}
}

View File

@ -98,6 +98,7 @@ pub struct Builder<'a> {
curr_file: Option<&'a str>,
validate_mode: bool,
pub assert_collector: AssertCollector,
strict: bool,
env: Rc<Val>,
// 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<P: Into<PathBuf>>(root: P, cache: Rc<RefCell<assets::Cache>>) -> Self {
Self::new_with_scope(root, cache, HashMap::new())
}
/// Constructs a new Builder with a provided scope.
pub fn new_with_scope<P: Into<PathBuf>>(
root: P,
cache: Rc<RefCell<assets::Cache>>,
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<P: Into<PathBuf>>(
root: P,
cache: Rc<RefCell<assets::Cache>>,
scope: ValueMap,
env: Rc<Val>,
) -> 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<Rc<Val>, Box<Error>> {
let mut new_fields = Vec::<(PositionedItem<String>, Rc<Val>)>::new();
@ -185,52 +231,6 @@ impl<'a> Builder<'a> {
}
}
/// Constructs a new Builder.
pub fn new<P: Into<PathBuf>>(root: P, cache: Rc<RefCell<assets::Cache>>) -> Self {
Self::new_with_scope(root, cache, HashMap::new())
}
/// Constructs a new Builder with a provided scope.
pub fn new_with_scope<P: Into<PathBuf>>(
root: P,
cache: Rc<RefCell<assets::Cache>>,
scope: ValueMap,
) -> Self {
let env_vars: Vec<(PositionedItem<String>, Rc<Val>)> = 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<P: Into<PathBuf>>(
root: P,
cache: Rc<RefCell<assets::Cache>>,
scope: ValueMap,
env: Rc<Val>,
) -> 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<Rc<Val>> {
let key = PositionedItem {
@ -442,6 +442,32 @@ impl<'a> Builder<'a> {
return None;
}
fn lookup_in_env(
&self,
search: &Token,
stack: &mut VecDeque<Rc<Val>>,
fs: &Vec<(String, String)>,
) -> Result<(), Box<Error>> {
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<Rc<Val>>,
@ -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,

View File

@ -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...");

View File

@ -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...");

View File

@ -46,6 +46,15 @@ impl JsonConverter {
Ok(serde_json::Value::Object(mp))
}
fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result<serde_json::Value> {
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<serde_json::Value> {
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)),
};

View File

@ -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)),
};

View File

@ -23,6 +23,17 @@ impl YamlConverter {
Ok(serde_yaml::Value::Sequence(v))
}
fn convert_env(&self, items: &Vec<(String, String)>) -> std::io::Result<serde_yaml::Value> {
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<String>, Rc<Val>)>,
@ -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)),
};