winbrew_database\config/
validation.rs

1use super::error::{ConfigError, ConfigResult};
2
3use super::{registry, types::Config};
4
5impl Config {
6    pub fn set_value(&mut self, key: &str, value: &str) -> ConfigResult<()> {
7        let key = key.trim();
8
9        if key.is_empty() {
10            return Err(ConfigError::EmptyKey);
11        }
12
13        let value = value.trim();
14        validate_config_value(key, value)?;
15        let value = value.to_string();
16
17        match key {
18            "core.log_level" => self.core.log_level = value,
19            "core.file_log_level" => self.core.file_log_level = value,
20            "core.auto_update" => self.core.auto_update = parse_bool(key, &value)?,
21            "core.confirm_remove" => self.core.confirm_remove = parse_bool(key, &value)?,
22            "core.default_yes" => self.core.default_yes = parse_bool(key, &value)?,
23            "core.color" => self.core.color = parse_bool(key, &value)?,
24            "paths.root" => self.paths.root = value,
25            "paths.packages" => self.paths.packages = value,
26            "paths.data" => self.paths.data = value,
27            "paths.logs" => self.paths.logs = value,
28            "paths.cache" => self.paths.cache = value,
29            _ => {
30                return Err(ConfigError::UnknownKey {
31                    key: key.to_string(),
32                });
33            }
34        }
35
36        Ok(())
37    }
38
39    pub fn unset_value(&mut self, key: &str) -> ConfigResult<()> {
40        let key = key.trim();
41
42        if key.is_empty() {
43            return Err(ConfigError::EmptyKey);
44        }
45
46        ensure_known_key(key)?;
47
48        let defaults = Config::default();
49
50        match key {
51            "core.log_level" => self.core.log_level = defaults.core.log_level,
52            "core.file_log_level" => self.core.file_log_level = defaults.core.file_log_level,
53            "core.auto_update" => self.core.auto_update = defaults.core.auto_update,
54            "core.confirm_remove" => self.core.confirm_remove = defaults.core.confirm_remove,
55            "core.default_yes" => self.core.default_yes = defaults.core.default_yes,
56            "core.color" => self.core.color = defaults.core.color,
57            "paths.root" => self.paths.root = defaults.paths.root,
58            "paths.packages" => self.paths.packages = defaults.paths.packages,
59            "paths.data" => self.paths.data = defaults.paths.data,
60            "paths.logs" => self.paths.logs = defaults.paths.logs,
61            "paths.cache" => self.paths.cache = defaults.paths.cache,
62            _ => {
63                return Err(ConfigError::UnknownKey {
64                    key: key.to_string(),
65                });
66            }
67        }
68
69        Ok(())
70    }
71}
72
73fn parse_bool(key: &str, value: &str) -> ConfigResult<bool> {
74    registry::parse_bool_value(value).ok_or_else(|| ConfigError::InvalidValue {
75        key: key.to_string(),
76        value: value.to_string(),
77    })
78}
79
80fn validate_config_value(key: &str, value: &str) -> ConfigResult<()> {
81    let def = ensure_known_key(key)?;
82
83    if let Some(validator) = def.validator {
84        validator(value).map_err(|source| ConfigError::Validation {
85            key: key.to_string(),
86            source,
87        })?;
88    }
89
90    Ok(())
91}
92
93fn ensure_known_key(key: &str) -> ConfigResult<&'static registry::KeyDef> {
94    registry::find(key).ok_or_else(|| ConfigError::UnknownKey {
95        key: key.to_string(),
96    })
97}
98
99#[cfg(test)]
100mod tests {
101    use super::{Config, ConfigError};
102
103    #[test]
104    fn set_value_accepts_boolean_aliases_in_validation() {
105        let mut config = Config::default();
106
107        config.set_value("core.auto_update", "yes").unwrap();
108        config.set_value("core.confirm_remove", "on").unwrap();
109        config.set_value("core.default_yes", "1").unwrap();
110
111        assert!(config.core.auto_update);
112        assert!(config.core.confirm_remove);
113        assert!(config.core.default_yes);
114    }
115
116    #[test]
117    fn set_value_accepts_env_filter_syntax_for_console_log_level() {
118        let mut config = Config::default();
119
120        config
121            .set_value("core.log_level", "winbrew=debug,info")
122            .unwrap();
123
124        assert_eq!(config.core.log_level, "winbrew=debug,info");
125    }
126
127    #[test]
128    fn unset_value_restores_default_values() {
129        let mut config = Config::default();
130
131        config.set_value("core.auto_update", "false").unwrap();
132        config.set_value("paths.packages", "C:\\custom").unwrap();
133
134        config.unset_value("core.auto_update").unwrap();
135        config.unset_value("paths.packages").unwrap();
136
137        let defaults = Config::default();
138
139        assert_eq!(config.core.auto_update, defaults.core.auto_update);
140        assert_eq!(config.paths.packages, defaults.paths.packages);
141    }
142
143    #[test]
144    fn unset_value_rejects_unknown_keys() {
145        let mut config = Config::default();
146
147        let err = config.unset_value("core.proxy").unwrap_err();
148
149        assert!(matches!(err, ConfigError::UnknownKey { .. }));
150    }
151}