diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-26 04:01:01 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-26 17:06:00 +0300 |
| commit | e9547a8e0de21d0ce81867b06511b68def16aff2 (patch) | |
| tree | 8a025301146434b79ff295ff0d0afc833c682502 /src/config | |
| parent | 3aa7903d28f94e67c14dd5cad48bbe942fbef2e9 (diff) | |
feat: refine live config lifecycle
Diffstat (limited to 'src/config')
| -rw-r--r-- | src/config/io.rs | 93 | ||||
| -rw-r--r-- | src/config/model.rs | 1 | ||||
| -rw-r--r-- | src/config/sound.rs | 4 |
3 files changed, 94 insertions, 4 deletions
diff --git a/src/config/io.rs b/src/config/io.rs index 2774fc93..16602972 100644 --- a/src/config/io.rs +++ b/src/config/io.rs @@ -33,6 +33,7 @@ impl Config { return LoadedConfig { config, diagnostics, + invalid_sections: Vec::new(), }; } Err(err) => { @@ -40,6 +41,7 @@ impl Config { return LoadedConfig { config: Self::default(), diagnostics: vec![format!("config parse error: {err}; using defaults")], + invalid_sections: Vec::new(), }; } }, @@ -48,6 +50,7 @@ impl Config { return LoadedConfig { config: Self::default(), diagnostics: vec![format!("config read error: {err}; using defaults")], + invalid_sections: Vec::new(), }; } } @@ -55,6 +58,7 @@ impl Config { LoadedConfig { config: Self::default(), diagnostics: Vec::new(), + invalid_sections: Vec::new(), } } } @@ -97,19 +101,104 @@ pub fn load_live_config() -> Result<LoadedConfig, Vec<String>> { return Ok(LoadedConfig { config: Config::default(), diagnostics: Vec::new(), + invalid_sections: Vec::new(), }); } let content = std::fs::read_to_string(&path) .map_err(|err| vec![format!("config read error: {err}; keeping current config")])?; - let config = toml::from_str::<Config>(&content) + load_live_config_from_str(&content) +} + +fn load_live_config_from_str(content: &str) -> Result<LoadedConfig, Vec<String>> { + let value = content + .parse::<toml::Value>() .map_err(|err| vec![format!("config parse error: {err}; keeping current config")])?; + let table = value.as_table().ok_or_else(|| { + vec![ + "config parse error: top-level config must be a table; keeping current config" + .to_string(), + ] + })?; + + let mut config = Config::default(); + let mut diagnostics = Vec::new(); + let mut invalid_sections = Vec::new(); + + if let Some(value) = table.get("onboarding") { + match value.clone().try_into::<Option<bool>>() { + Ok(onboarding) => config.onboarding = onboarding, + Err(err) => diagnostics.push(format!( + "invalid onboarding setting: {err}; keeping current onboarding state" + )), + } + } + + load_live_section( + table, + "theme", + "theme config", + &mut diagnostics, + &mut invalid_sections, + |section| config.theme = section, + ); + load_live_section( + table, + "keys", + "keybinding config", + &mut diagnostics, + &mut invalid_sections, + |section| config.keys = section, + ); + load_live_section( + table, + "ui", + "ui config", + &mut diagnostics, + &mut invalid_sections, + |section| config.ui = section, + ); + load_live_section( + table, + "advanced", + "advanced config", + &mut diagnostics, + &mut invalid_sections, + |section| config.advanced = section, + ); + Ok(LoadedConfig { config, - diagnostics: Vec::new(), + diagnostics, + invalid_sections, }) } +fn load_live_section<T>( + table: &toml::map::Map<String, toml::Value>, + section: &'static str, + label: &str, + diagnostics: &mut Vec<String>, + invalid_sections: &mut Vec<String>, + apply: impl FnOnce(T), +) where + T: serde::de::DeserializeOwned, +{ + let Some(value) = table.get(section) else { + return; + }; + + match value.clone().try_into::<T>() { + Ok(section_config) => apply(section_config), + Err(err) => { + diagnostics.push(format!( + "invalid {label}: {err}; keeping current {section} settings" + )); + invalid_sections.push(section.to_string()); + } + } +} + pub(crate) fn upsert_top_level_bool(content: &str, key: &str, value: bool) -> String { let replacement = format!("{key} = {value}"); let mut lines: Vec<String> = content.lines().map(|line| line.to_string()).collect(); diff --git a/src/config/model.rs b/src/config/model.rs index 32d60493..5a17bea7 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -44,6 +44,7 @@ pub struct Config { pub struct LoadedConfig { pub config: Config, pub diagnostics: Vec<String>, + pub invalid_sections: Vec<String>, } #[derive(Debug, Deserialize)] diff --git a/src/config/sound.rs b/src/config/sound.rs index 063a521b..6219d698 100644 --- a/src/config/sound.rs +++ b/src/config/sound.rs @@ -6,7 +6,7 @@ use crate::detect::Agent; use super::io::resolve_config_relative_path; -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] #[serde(default)] pub struct SoundConfig { pub enabled: bool, @@ -22,7 +22,7 @@ pub struct SoundConfig { pub agents: AgentSoundOverrides, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] #[serde(default)] pub struct AgentSoundOverrides { pub pi: AgentSoundSetting, |
