use super::types::*;
use super::utils;
use strfmt;
lazy_static!{
pub static ref DEFAULT_GLOBALS: HashSet<String> = HashSet::from_iter(
["repo", "cwd"].iter().map(|s| s.to_string()));
}
pub fn resolve_default_vars(vars: &Variables, fpath: &Path,
variables: &mut Variables,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> LoadResult<()> {
let cwd = fpath.parent().unwrap();
let mut fmtvars = Variables::new();
fmtvars.insert("cwd".to_string(), cwd.to_str().unwrap().to_string());
try!(utils::find_and_insert_repo(cwd, repo_map));
fmtvars.insert("repo".to_string(), repo_map.get(cwd).unwrap()
.to_str().unwrap().to_string());
let mut error = false;
for (k, v) in vars {
let var = match utils::strfmt_ignore_missing(v.as_str(), &fmtvars) {
Ok(v) => v,
Err(e) => {
error!("error formatting: {}", e.to_string());
error = true;
continue;
}
};
if variables.insert(k.clone(), var).is_some() {
error!("global var {:?} exists twice, one at {:?}", k, fpath);
error = true;
}
}
if error {
return Err(LoadError::new("errors while resolving default variables".to_string()));
}
Ok(())
}
pub fn resolve_vars(variables: &mut Variables) -> LoadResult<()> {
let mut msg = String::new();
let mut keys: Vec<String> = variables.keys().cloned().collect();
let mut errors = Vec::new();
let mut num_changed;
let mut remove_keys = DEFAULT_GLOBALS.clone();
loop {
keys = keys.iter().filter(|k| !remove_keys.contains(k.as_str()))
.cloned().collect();
num_changed = 0;
errors.clear();
remove_keys.clear();
for k in &keys {
let var = variables.remove(k.as_str()).unwrap();
match strfmt::strfmt(var.as_str(), variables) {
Ok(s) => {
if var != s {
num_changed += 1;
} else {
remove_keys.insert(k.clone());
}
variables.insert(k.clone(), s);
}
Err(e) => match e {
strfmt::FmtError::Invalid(e) | strfmt::FmtError::TypeError(e) => {
return Err(LoadError::new(e.to_string()));
},
strfmt::FmtError::KeyError(_) => {
errors.push(k.clone());
variables.insert(k.clone(), var);
},
}
}
}
if num_changed == 0 { if errors.is_empty() {
break;
} else {
keys = keys.iter().filter(|k| !remove_keys.contains(k.as_str()))
.cloned().collect();
write!(msg, "Could not resolve some globals: {:?}\ngot related errors: {:?}",
keys, errors).unwrap();
return Err(LoadError::new(msg));
}
}
}
Ok(())
}
pub fn fill_text_fields(artifacts: &mut Artifacts,
variables: &mut Variables,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> LoadResult<()> {
let mut error = false;
let mut errors: Vec<(&str, strfmt::FmtError)> = Vec::new();
for (name, art) in artifacts.iter_mut() {
trace!("filling in {}", name);
errors.clear();
let cwd = art.path.parent().expect("no-path-parent").to_path_buf();
try!(utils::find_and_insert_repo(&cwd, repo_map));
variables.insert("cwd".to_string(), cwd.to_str().expect("utf-path").to_string());
variables.insert("repo".to_string(), repo_map.get(&cwd).expect("repo_map")
.to_str().expect("utf-path").to_string());
match strfmt::strfmt(art.text.as_str(), variables) {
Ok(t) => art.text = t,
Err(err) => errors.push(("text field", err)),
};
if !errors.is_empty() {
error!(" resolving variables on [{:?}] {} failed: {:?}", art.path, name, errors);
error = true;
}
}
if error {
return Err(LoadError::new("failure to resolve artifact text fields".to_string()));
}
trace!("Done filling");
Ok(())
}
pub fn resolve_loaded_vars(mut loaded_vars: Vec<(PathBuf, Variables)>,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> LoadResult<Variables> {
let mut variables = Variables::new();
debug!("Resolving default globals in variables, see SPC-vars.1");
for pv in loaded_vars.drain(0..) {
let p = pv.0;
let v = pv.1;
try!(resolve_default_vars(&v, p.as_path(), &mut variables, repo_map));
}
debug!("Resolving variables, see SPC-vars.2");
try!(resolve_vars(&mut variables));
Ok(variables)
}