steamkit_kv/
lib.rs

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        // check if input is empty
64        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        // calculate necessary map capacity
136        let mut capacity = 0;
137
138        // since this is just a guess, we don't care above duplicate keys
139        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        // this is a guess at the capacity, not sure what needs more than 10 levels of nesting
170        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        // check if input is empty
180        if !input.is_empty() {
181            return Err(Error::UnexpectedInput(input.to_string()));
182        }
183
184        // calculate necessary map capacity
185        let mut capacity = 0;
186
187        // since this is just a guess, we don't care above duplicate keys
188        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                    // BUG: if a lower-level key is inserted after a higher-level key, the higher level key will stay in the map
209                    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            // remove the last key since it's done processing
221            path.pop();
222        }
223
224        // this is a guess at the capacity, not sure what needs more than 10 levels of nesting
225        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}