use std::path::PathBuf;
use toml::Value;
use clap::App;
use failure::ResultExt;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use libimagerror::errors::ErrorMsg as EM;
pub fn fetch_config(searchpath: &PathBuf) -> Result<Option<Value>> {
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::io::stderr;
use libimagutil::variants::generate_variants as gen_vars;
use libimagerror::trace::trace_error;
let variants : Vec<&'static str> = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
let modifier = |base: &PathBuf, v: &&str| {
let mut base = base.clone();
base.push(String::from(*v));
base
};
let vals = vec![
vec![searchpath.clone()],
gen_vars(searchpath, variants.iter(), &modifier),
env::var("HOME")
.map(|home| gen_vars(&PathBuf::from(home), variants.iter(), &modifier))
.unwrap_or_else(|_| vec![]),
xdg_basedir::get_data_home()
.map(|data_dir| gen_vars(&data_dir, variants.iter(), &modifier))
.unwrap_or_else(|_| vec![]),
];
let config = vals
.iter()
.flatten()
.filter(|path| path.exists() && path.is_file())
.filter_map(|path| {
let content = {
let f = File::open(path);
if f.is_err() {
let _ = write!(stderr(), "Error opening file: {:?}", f);
return None
}
let mut f = f.unwrap();
let mut s = String::new();
f.read_to_string(&mut s).ok();
s
};
::toml::de::from_str::<::toml::Value>(&content[..])
.map(Some)
.unwrap_or_else(|e| {
let line_col = e
.line_col()
.map(|(line, col)| format!("Line {}, Column {}", line, col))
.unwrap_or_else(|| String::from("Line unknown, Column unknown"));
let _ = write!(stderr(), "Config file parser error at {}", line_col);
let e = Error::from(EM::TomlDeserError);
trace_error(&e);
None
})
})
.nth(0);
Ok(config)
}
pub fn override_config(val: &mut Value, v: Vec<String>) -> Result<()> {
use libimagutil::key_value_split::*;
use toml_query::read::TomlValueReadExt;
v.into_iter()
.map(|s| { debug!("Trying to process '{}'", s); s })
.filter_map(|s| s.into_kv().map(Into::into).or_else(|| {
warn!("Could split at '=' - will be ignore override");
None
}))
.map(|(k, v)| {
let value = val.read_mut(&k)
.context(EM::TomlQueryError)?
.ok_or_else(|| err_msg("No config value there, cannot override."))?;
let new_value = into_value(value, v)
.ok_or_else(|| err_msg("Config override type not matching"))?;
info!("Successfully overridden: {} = {}", k, new_value);
*value = new_value;
Ok(())
})
.map(|elem: Result<()>| elem.context(err_msg("Config override error")).map_err(Error::from))
.collect::<Result<()>>()
}
fn into_value(value: &Value, s: String) -> Option<Value> {
use std::str::FromStr;
match *value {
Value::String(_) => Some(Value::String(s)),
Value::Integer(_) => FromStr::from_str(&s[..]).ok().map(Value::Integer),
Value::Float(_) => FromStr::from_str(&s[..]).ok().map(Value::Float),
Value::Boolean(_) => {
if s == "true" { Some(Value::Boolean(true)) }
else if s == "false" { Some(Value::Boolean(false)) }
else { None }
}
Value::Datetime(_) => Value::try_from(s).ok(),
_ => None,
}
}
pub trait InternalConfiguration {
fn enable_logging(&self) -> bool {
true
}
fn use_inmemory_fs(&self) -> bool {
false
}
}
impl<'a> InternalConfiguration for App<'a, 'a> {}