1mod error;
2mod parser;
3
4use std::collections::HashMap;
5
6pub use error::*;
7use indexmap::IndexMap;
8
9pub type Path = Vec<String>;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum KeyValue {
13 String(String),
14 Map(IndexMap<String, Self>),
15}
16
17impl From<String> for KeyValue {
18 fn from(value: String) -> Self {
19 Self::String(value)
20 }
21}
22
23impl From<&str> for KeyValue {
24 fn from(value: &str) -> Self {
25 Self::String(value.to_string())
26 }
27}
28
29fn merge(entries: Vec<parser::Entry>) -> IndexMap<String, KeyValue> {
30 let mut map = IndexMap::new();
31
32 for entry in entries {
33 match map.entry(entry.key) {
34 indexmap::map::Entry::Occupied(mut occupied) => match entry.value {
35 parser::Value::String(s) => {
36 *occupied.get_mut() = KeyValue::String(s);
37 }
38 parser::Value::Map(new_entries) => {
39 if let KeyValue::Map(existing_map) = occupied.get_mut() {
40 existing_map.extend(merge(new_entries));
41 } else {
42 *occupied.get_mut() = KeyValue::Map(merge(new_entries));
43 }
44 }
45 },
46 indexmap::map::Entry::Vacant(vacant) => {
47 let kv = match entry.value {
48 parser::Value::String(s) => KeyValue::String(s),
49 parser::Value::Map(v) => KeyValue::Map(merge(v)),
50 };
51 vacant.insert(kv);
52 }
53 }
54 }
55
56 map
57}
58
59impl KeyValue {
60 pub fn parse(input: &str) -> Result<Self> {
61 let (input, entry) = parser::key_value(input.trim()).map_err(|_| Error::Parse)?;
62
63 if !input.is_empty() {
65 return Err(Error::UnexpectedInput(input.to_string()));
66 }
67
68 Ok(Self::Map(merge(vec![entry])))
69 }
70
71 pub fn get<I, T>(&self, path: I) -> Option<&Self>
72 where
73 I: IntoIterator<Item = T>,
74 T: AsRef<str>,
75 {
76 let mut iter = path.into_iter().peekable();
77 let path = iter.next();
78
79 if let Some(path) = path {
80 let path = path.as_ref();
81 match self {
82 Self::String(_) => None,
83 Self::Map(map) => match map.get(path) {
84 Some(kv) => {
85 if iter.peek().is_some() {
86 kv.get(iter.collect::<Vec<_>>())
87 } else {
88 Some(kv)
89 }
90 }
91 None => None,
92 },
93 }
94 } else {
95 Some(self)
96 }
97 }
98
99 pub fn get_mut<I, T>(&mut self, path: I) -> Option<&mut Self>
100 where
101 I: IntoIterator<Item = T>,
102 T: AsRef<str>,
103 {
104 let mut iter = path.into_iter().peekable();
105 let path = iter.next();
106
107 if let Some(path) = path {
108 let path = path.as_ref();
109 match self {
110 Self::String(_) => None,
111 Self::Map(map) => match map.get_mut(path) {
112 Some(kv) => {
113 if iter.peek().is_some() {
114 kv.get_mut(iter.collect::<Vec<_>>())
115 } else {
116 Some(kv)
117 }
118 }
119 None => None,
120 },
121 }
122 } else {
123 Some(self)
124 }
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct FlatKeyValues {
130 map: HashMap<Path, String>,
131}
132
133impl From<KeyValue> for FlatKeyValues {
134 fn from(kv: KeyValue) -> Self {
135 let mut capacity = 0;
137
138 fn calculate_capacity(kv: &KeyValue, capacity: &mut usize) {
140 match kv {
141 KeyValue::String(_) => *capacity += 1,
142 KeyValue::Map(entries) => {
143 for kv in entries.values() {
144 calculate_capacity(kv, capacity);
145 }
146 }
147 }
148 }
149
150 calculate_capacity(&kv, &mut capacity);
151
152 let mut map = HashMap::with_capacity(capacity);
153
154 fn process(map: &mut HashMap<Path, String>, kv: KeyValue, path: &mut Path) {
155 match kv {
156 KeyValue::String(s) => {
157 map.insert(path.clone(), s);
158 }
159 KeyValue::Map(entries) => {
160 for (key, kv) in entries {
161 path.push(key);
162 process(map, kv, path);
163 path.pop();
164 }
165 }
166 }
167 }
168
169 process(&mut map, kv, &mut Vec::with_capacity(10));
171
172 Self { map }
173 }
174}
175
176impl FlatKeyValues {
177 pub fn parse(input: &str) -> Result<Self> {
178 let (input, entry) = parser::key_value(input.trim()).map_err(|_| Error::Parse)?;
179 if !input.is_empty() {
181 return Err(Error::UnexpectedInput(input.to_string()));
182 }
183
184 let mut capacity = 0;
186
187 fn calculate_capacity(entry: &parser::Entry, capacity: &mut usize) {
189 match &entry.value {
190 parser::Value::String(_) => *capacity += 1,
191 parser::Value::Map(entries) => {
192 for entry in entries {
193 calculate_capacity(entry, capacity);
194 }
195 }
196 }
197 }
198
199 calculate_capacity(&entry, &mut capacity);
200
201 let mut map = HashMap::with_capacity(capacity);
202
203 fn process(map: &mut HashMap<Path, String>, entry: parser::Entry, path: &mut Path) {
204 path.push(entry.key);
205
206 match entry.value {
207 parser::Value::String(s) => {
208 map.insert(path.clone(), s);
210 }
211 parser::Value::Map(entries) => {
212 map.remove(path);
213
214 for entry in entries {
215 process(map, entry, path);
216 }
217 }
218 }
219
220 path.pop();
222 }
223
224 process(&mut map, entry, &mut Vec::with_capacity(10));
226
227 Ok(Self { map })
228 }
229
230 pub fn get<I, T>(&self, path: I) -> Option<&String>
231 where
232 I: IntoIterator<Item = T>,
233 T: AsRef<str>,
234 {
235 let path = path
236 .into_iter()
237 .map(|s| s.as_ref().to_string())
238 .collect::<Vec<_>>();
239 self.map.get(&path)
240 }
241
242 pub fn get_str<I, T>(&self, path: I) -> Option<&str>
243 where
244 I: IntoIterator<Item = T>,
245 T: AsRef<str>,
246 {
247 self.get(path).map(|s| s.as_str())
248 }
249
250 pub fn get_mut<I, T>(&mut self, path: I) -> Option<&mut String>
251 where
252 I: IntoIterator<Item = T>,
253 T: AsRef<str>,
254 {
255 let path = path
256 .into_iter()
257 .map(|s| s.as_ref().to_string())
258 .collect::<Vec<_>>();
259 self.map.get_mut(&path)
260 }
261
262 pub fn is_empty(&self) -> bool {
263 self.map.is_empty()
264 }
265
266 pub fn len(&self) -> usize {
267 self.map.len()
268 }
269
270 pub fn iter(&self) -> impl Iterator<Item = (&Path, &String)> {
271 self.map.iter()
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 const LARGE_DATA: &str = include_str!("../assets/items_game.txt");
280
281 #[test]
282 fn nested() {
283 let input = r#"
284 "key1"
285 {
286 "key2" "value1"
287 "key3"
288 {
289 "key4" "value2"
290 }
291 }
292 "#;
293
294 let kv = KeyValue::parse(input).unwrap();
295 assert_eq!(
296 kv.get(["key1", "key2"]),
297 Some(&KeyValue::String("value1".into()))
298 );
299 assert_eq!(
300 kv.get(["key1", "key3", "key4"]),
301 Some(&KeyValue::String("value2".into()))
302 );
303 }
304
305 #[test]
306 fn nested_duplicate_keys() {
307 let input = r#"
308 "root"
309 {
310 "key1" "value1"
311 "key1" "value2"
312 "key2" "value3"
313 }
314 "#;
315
316 let kv = KeyValue::parse(input).unwrap();
317 assert_eq!(
318 kv.get(["root", "key1"]),
319 Some(&KeyValue::String("value2".into()))
320 );
321 assert_eq!(
322 kv.get(["root", "key2"]),
323 Some(&KeyValue::String("value3".into()))
324 );
325 }
326
327 #[test]
328 fn nested_large() {
329 let kv = KeyValue::parse(LARGE_DATA).unwrap();
330 assert_eq!(
331 kv.get(["items_game", "game_info", "first_valid_item_slot"]),
332 Some(&KeyValue::String("0".into()))
333 );
334 assert_eq!(
335 kv.get(["items_game", "items", "507", "name"]),
336 Some(&KeyValue::String("weapon_knife_karambit".into()))
337 );
338 }
339
340 #[test]
341 fn flat() {
342 let input = r#"
343 "key1"
344 {
345 "key2" "value1"
346 "key3"
347 {
348 "key4" "value2"
349 }
350 }
351 "#;
352
353 let kv = FlatKeyValues::parse(input).unwrap();
354 assert_eq!(kv.get_str(["key1", "key2"]), Some("value1"));
355 assert_eq!(kv.get_str(["key1", "key3", "key4"]), Some("value2"));
356 }
357
358 #[test]
359 fn flat_duplicate_keys() {
360 let input = r#"
361 "root"
362 {
363 "key1" "value1"
364 "key1" "value2"
365 "key2" "value3"
366 }
367 "#;
368
369 let kv = FlatKeyValues::parse(input).unwrap();
370 assert_eq!(kv.get_str(["root", "key1"]), Some("value2"));
371 assert_eq!(kv.get_str(["root", "key2"]), Some("value3"));
372 }
373
374 #[test]
375 fn flat_large() {
376 let kv = FlatKeyValues::parse(LARGE_DATA).unwrap();
377 assert_eq!(
378 kv.get_str(["items_game", "game_info", "first_valid_item_slot"]),
379 Some("0")
380 );
381 assert_eq!(
382 kv.get_str(["items_game", "items", "507", "name"]),
383 Some("weapon_knife_karambit")
384 );
385 }
386
387 #[test]
388 fn flat_from_nested_large() {
389 let kv = FlatKeyValues::from(KeyValue::parse(LARGE_DATA).unwrap());
390 assert_eq!(
391 kv.get_str(["items_game", "game_info", "first_valid_item_slot"]),
392 Some("0")
393 );
394 assert_eq!(
395 kv.get_str(["items_game", "items", "507", "name"]),
396 Some("weapon_knife_karambit")
397 );
398 }
399}