use dev_prefix::*;
use super::types::*;
use super::utils;
use strfmt;
lazy_static!{
pub static ref DEFAULT_GLOBALS: HashSet<String> = HashSet::from_iter(
[REPO_VAR, CWD_VAR].iter().map(|s| s.to_string()));
}
pub fn resolve_default_vars(vars: &Variables,
fpath: &Path,
variables: &mut Variables,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> Result<()> {
let cwd = fpath.parent().unwrap();
let mut fmtvars = Variables::new();
fmtvars.insert(CWD_VAR.to_string(), cwd.to_str().unwrap().to_string());
try!(utils::find_and_insert_repo(cwd, repo_map));
fmtvars.insert(REPO_VAR.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(ErrorKind::InvalidVariable("errors while resolving default variables"
.to_string())
.into());
}
Ok(())
}
pub fn resolve_vars(variables: &mut Variables) -> Result<()> {
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(_) |
strfmt::FmtError::TypeError(_) => return Err(ErrorKind::StrFmt(e).into()),
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();
let msg = format!("Could not resolve some globals: {:?}\ngot related errors: {:?}",
keys,
errors);
return Err(ErrorKind::InvalidVariable(msg).into());
}
}
}
Ok(())
}
pub fn fill_text_fields(artifacts: &mut Artifacts,
variables: &mut Variables,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> Result<()> {
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_VAR.to_string(),
cwd.to_str().expect("utf-path").to_string());
variables.insert(REPO_VAR.to_string(),
repo_map.get(&cwd)
.expect("repo_map")
.to_str()
.expect("utf-path")
.to_string());
match strfmt::strfmt(art.text.value.as_str(), variables) {
Ok(t) => art.text.value = t,
Err(err) => errors.push(("text field", err)),
};
if !errors.is_empty() {
error!(" resolving variables on [{:?}] {} failed: {:?}",
art.path,
name,
errors);
error = true;
}
}
variables.remove(CWD_VAR);
variables.remove(REPO_VAR);
if error {
return Err(ErrorKind::InvalidTextVariables.into());
}
trace!("Done filling");
Ok(())
}
pub fn resolve_loaded_vars(variables_map: &HashMap<PathBuf, Variables>,
repo_map: &mut HashMap<PathBuf, PathBuf>)
-> Result<Variables> {
let mut variables = Variables::new();
debug!("Resolving default globals in variables, see SPC-vars.1");
for pv in variables_map.iter() {
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)
}