use {
serde::{Deserialize, Serialize},
std::path::Path,
};
use crate::{
command::{AfterRun, Shell},
discover,
on_disk::OnDisk,
};
const CONFIG_FILE: &str = ".config.toml";
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_shell: Option<Shell>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default_after_run: Option<AfterRun>,
}
impl Config {
pub fn load_merged(start_dir: &Path) -> Self {
let dirs = discover::discover_iforgor_dirs(start_dir);
let mut merged = Config::default();
for dir in &dirs {
let config_path = dir.join(CONFIG_FILE);
match OnDisk::<Config>::open(config_path.clone()) {
Ok(config) => {
if merged.default_shell.is_none() {
merged.default_shell = config.default_shell.clone();
}
if merged.default_after_run.is_none() {
merged.default_after_run = config.default_after_run.clone();
}
}
Err(e) if config_path.exists() => {
eprintln!("Warning: failed to parse {}: {e}", config_path.display());
}
_ => {}
}
}
merged
}
}
#[cfg(test)]
mod tests {
use {super::*, std::fs};
#[test]
fn parse_empty_config() {
let config: Config = toml::from_str("").unwrap();
assert!(config.default_shell.is_none());
assert!(config.default_after_run.is_none());
}
#[test]
fn parse_default_shell() {
let config: Config = toml::from_str(r#"default_shell = "bash""#).unwrap();
assert!(config.default_shell.is_some());
}
#[test]
fn parse_default_after_run() {
let config: Config = toml::from_str(r#"default_after_run = "wait""#).unwrap();
assert!(matches!(config.default_after_run, Some(AfterRun::Wait)));
}
#[test]
fn parse_full_config() {
let config: Config = toml::from_str(
r#"
default_shell = "zsh"
default_after_run = { Delay = 3 }
"#,
)
.unwrap();
assert!(config.default_shell.is_some());
assert!(matches!(config.default_after_run, Some(AfterRun::Delay(3))));
}
#[test]
fn load_merged_closest_wins() {
let tmp = tempfile::tempdir().unwrap();
let project_iforgor = tmp.path().join(".iforgor");
let sub_iforgor = tmp.path().join("sub").join(".iforgor");
fs::create_dir_all(&project_iforgor).unwrap();
fs::create_dir_all(&sub_iforgor).unwrap();
fs::write(
project_iforgor.join(".config.toml"),
r#"default_shell = "bash"
default_after_run = "wait""#,
)
.unwrap();
fs::write(sub_iforgor.join(".config.toml"), r#"default_shell = "zsh""#).unwrap();
let config = Config::load_merged(&tmp.path().join("sub"));
assert!(matches!(
config.default_shell,
Some(Shell::Predefined(crate::command::PredefinedShell::Zsh))
));
assert!(matches!(config.default_after_run, Some(AfterRun::Wait)));
}
}