oxihuman_core/
config_reader.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
6
7#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct ConfigReader {
11 values: HashMap<String, String>,
12 sections: Vec<String>,
13}
14
15impl Default for ConfigReader {
16 fn default() -> Self {
17 Self::new()
18 }
19}
20
21#[allow(dead_code)]
22impl ConfigReader {
23 pub fn new() -> Self {
24 Self {
25 values: HashMap::new(),
26 sections: Vec::new(),
27 }
28 }
29
30 pub fn parse(text: &str) -> Self {
31 let mut reader = Self::new();
32 let mut current_section = String::new();
33 for line in text.lines() {
34 let trimmed = line.trim();
35 if trimmed.is_empty() || trimmed.starts_with('#') || trimmed.starts_with(';') {
36 continue;
37 }
38 if trimmed.starts_with('[') && trimmed.ends_with(']') {
39 current_section = trimmed[1..trimmed.len() - 1].to_string();
40 if !reader.sections.contains(¤t_section) {
41 reader.sections.push(current_section.clone());
42 }
43 continue;
44 }
45 if let Some((key, value)) = trimmed.split_once('=') {
46 let full_key = if current_section.is_empty() {
47 key.trim().to_string()
48 } else {
49 format!("{}.{}", current_section, key.trim())
50 };
51 reader.values.insert(full_key, value.trim().to_string());
52 }
53 }
54 reader
55 }
56
57 pub fn get(&self, key: &str) -> Option<&str> {
58 self.values.get(key).map(|s| s.as_str())
59 }
60
61 pub fn get_or(&self, key: &str, default: &str) -> String {
62 self.values
63 .get(key)
64 .cloned()
65 .unwrap_or_else(|| default.to_string())
66 }
67
68 pub fn get_int(&self, key: &str) -> Option<i64> {
69 self.values.get(key).and_then(|v| v.parse().ok())
70 }
71
72 pub fn get_float(&self, key: &str) -> Option<f64> {
73 self.values.get(key).and_then(|v| v.parse().ok())
74 }
75
76 pub fn get_bool(&self, key: &str) -> Option<bool> {
77 self.values.get(key).and_then(|v| match v.as_str() {
78 "true" | "1" | "yes" => Some(true),
79 "false" | "0" | "no" => Some(false),
80 _ => None,
81 })
82 }
83
84 pub fn contains(&self, key: &str) -> bool {
85 self.values.contains_key(key)
86 }
87
88 pub fn count(&self) -> usize {
89 self.values.len()
90 }
91
92 pub fn sections(&self) -> &[String] {
93 &self.sections
94 }
95
96 pub fn keys_in_section(&self, section: &str) -> Vec<String> {
97 let prefix = format!("{section}.");
98 self.values
99 .keys()
100 .filter(|k| k.starts_with(&prefix))
101 .cloned()
102 .collect()
103 }
104
105 pub fn all_keys(&self) -> Vec<&str> {
106 self.values.keys().map(|k| k.as_str()).collect()
107 }
108
109 pub fn set(&mut self, key: &str, value: &str) {
110 self.values.insert(key.to_string(), value.to_string());
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_empty() {
120 let r = ConfigReader::new();
121 assert_eq!(r.count(), 0);
122 }
123
124 #[test]
125 fn test_parse_simple() {
126 let r = ConfigReader::parse("key=value\nfoo=bar");
127 assert_eq!(r.get("key"), Some("value"));
128 assert_eq!(r.get("foo"), Some("bar"));
129 }
130
131 #[test]
132 fn test_parse_sections() {
133 let r = ConfigReader::parse("[section]\nk=v");
134 assert_eq!(r.get("section.k"), Some("v"));
135 assert_eq!(r.sections(), &["section"]);
136 }
137
138 #[test]
139 fn test_comments_ignored() {
140 let r = ConfigReader::parse("# comment\n; another\nk=v");
141 assert_eq!(r.count(), 1);
142 }
143
144 #[test]
145 fn test_get_int() {
146 let r = ConfigReader::parse("n=42");
147 assert_eq!(r.get_int("n"), Some(42));
148 }
149
150 #[test]
151 fn test_get_float() {
152 let r = ConfigReader::parse("f=2.75");
153 let v = r.get_float("f").expect("should succeed");
154 assert!((v - 2.75).abs() < 1e-6);
155 }
156
157 #[test]
158 fn test_get_bool() {
159 let r = ConfigReader::parse("a=true\nb=false\nc=yes");
160 assert_eq!(r.get_bool("a"), Some(true));
161 assert_eq!(r.get_bool("b"), Some(false));
162 assert_eq!(r.get_bool("c"), Some(true));
163 }
164
165 #[test]
166 fn test_get_or_default() {
167 let r = ConfigReader::new();
168 assert_eq!(r.get_or("missing", "default"), "default");
169 }
170
171 #[test]
172 fn test_contains() {
173 let r = ConfigReader::parse("k=v");
174 assert!(r.contains("k"));
175 assert!(!r.contains("x"));
176 }
177
178 #[test]
179 fn test_set() {
180 let mut r = ConfigReader::new();
181 r.set("k", "v");
182 assert_eq!(r.get("k"), Some("v"));
183 }
184}