winbrew_database\config/
registry.rs

1use tracing_subscriber::EnvFilter;
2
3use super::error::ConfigValidationError;
4
5pub type Validator = fn(&str) -> std::result::Result<(), ConfigValidationError>;
6
7pub struct KeyDef {
8    pub key: &'static str,
9    pub validator: Option<Validator>,
10}
11
12pub static KEYS: &[KeyDef] = &[
13    KeyDef {
14        key: "core.log_level",
15        validator: Some(|value| {
16            EnvFilter::try_new(value).map_err(|err| ConfigValidationError::InvalidLogLevel {
17                value: value.to_string(),
18                reason: err.to_string(),
19            })?;
20            Ok(())
21        }),
22    },
23    KeyDef {
24        key: "core.file_log_level",
25        validator: Some(|value| {
26            EnvFilter::try_new(value).map_err(|err| {
27                ConfigValidationError::InvalidFileLogLevel {
28                    value: value.to_string(),
29                    reason: err.to_string(),
30                }
31            })?;
32            Ok(())
33        }),
34    },
35    KeyDef {
36        key: "core.auto_update",
37        validator: Some(validate_bool),
38    },
39    KeyDef {
40        key: "core.confirm_remove",
41        validator: Some(validate_bool),
42    },
43    KeyDef {
44        key: "core.default_yes",
45        validator: Some(validate_bool),
46    },
47    KeyDef {
48        key: "core.color",
49        validator: Some(validate_bool),
50    },
51    KeyDef {
52        key: "paths.root",
53        validator: None,
54    },
55    KeyDef {
56        key: "paths.packages",
57        validator: None,
58    },
59    KeyDef {
60        key: "paths.data",
61        validator: None,
62    },
63    KeyDef {
64        key: "paths.logs",
65        validator: None,
66    },
67    KeyDef {
68        key: "paths.cache",
69        validator: None,
70    },
71];
72
73pub fn find(key: &str) -> Option<&'static KeyDef> {
74    KEYS.iter().find(|def| def.key == key)
75}
76
77pub fn suggest_key(key: &str) -> Option<&'static str> {
78    let key = key.trim();
79
80    if key.is_empty() {
81        return None;
82    }
83
84    if let Some(exact) = KEYS.iter().find(|def| def.key.eq_ignore_ascii_case(key)) {
85        return Some(exact.key);
86    }
87
88    if key.contains('.') {
89        return None;
90    }
91
92    for section in ["core", "paths"] {
93        let candidate = format!("{section}.{key}");
94
95        if let Some(exact) = KEYS
96            .iter()
97            .find(|def| def.key.eq_ignore_ascii_case(&candidate))
98        {
99            return Some(exact.key);
100        }
101    }
102
103    None
104}
105
106fn validate_bool(value: &str) -> std::result::Result<(), ConfigValidationError> {
107    parse_bool_value(value)
108        .map(|_| ())
109        .ok_or_else(|| ConfigValidationError::ExpectedBoolean {
110            value: value.to_string(),
111        })
112}
113
114pub(super) fn parse_bool_value(value: &str) -> Option<bool> {
115    match value {
116        "true" | "1" | "yes" | "on" => Some(true),
117        "false" | "0" | "no" | "off" => Some(false),
118        _ => None,
119    }
120}