use crate::ConfigError;
use std::path::{Path, PathBuf};
pub fn merge_tables(base: &mut toml::Table, overlay: toml::Table) {
for (key, overlay_value) in overlay {
match (base.get_mut(&key), overlay_value) {
(Some(toml::Value::Table(base_table)), toml::Value::Table(overlay_table)) => {
merge_tables(base_table, overlay_table);
},
(_, overlay_value) => {
base.insert(key, overlay_value);
},
}
}
}
pub fn read_toml_table<P: AsRef<Path>>(path: P) -> Result<toml::Table, ConfigError> {
let path = path.as_ref();
let location = path.display().to_string();
let contents = std::fs::read_to_string(path).map_err(|e| ConfigError::Read {
location: location.clone(),
error: e.to_string(),
})?;
toml::from_str::<toml::Table>(&contents).map_err(|e| ConfigError::Parse {
location,
error: e.to_string(),
})
}
pub fn find_config_files_from(start: &Path, file_name: &str) -> Vec<PathBuf> {
let mut files = Vec::new();
let mut dir = start.to_path_buf();
loop {
let candidate = dir.join(file_name);
if candidate.is_file() {
files.push(candidate);
}
if !dir.pop() {
break;
}
}
files.reverse();
files
}
pub fn user_config_file(app_name: &str, file_name: &str) -> Option<PathBuf> {
dirs::config_local_dir().map(|dir| dir.join(app_name).join(file_name))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn merge_overrides_scalars_and_merges_subtables() {
let mut base: toml::Table = toml::from_str("a = 1\n[t]\nx = 1\ny = 1\n").unwrap();
let overlay: toml::Table = toml::from_str("a = 2\n[t]\ny = 9\n").unwrap();
merge_tables(&mut base, overlay);
assert_eq!(base["a"].as_integer(), Some(2));
let t = base["t"].as_table().unwrap();
assert_eq!(t["x"].as_integer(), Some(1));
assert_eq!(t["y"].as_integer(), Some(9));
}
#[test]
fn find_files_returns_root_first() {
let tmp = std::env::temp_dir().join(format!("rudof_cfg_disc_{}", std::process::id()));
let nested = tmp.join("a").join("b");
std::fs::create_dir_all(&nested).unwrap();
std::fs::write(tmp.join("rudof.toml"), "x = 1\n").unwrap();
std::fs::write(nested.join("rudof.toml"), "x = 2\n").unwrap();
let files = find_config_files_from(&nested, "rudof.toml");
assert_eq!(files.first().unwrap(), &tmp.join("rudof.toml"));
assert_eq!(files.last().unwrap(), &nested.join("rudof.toml"));
std::fs::remove_dir_all(&tmp).ok();
}
}