keyvalues_parser/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(unknown_lints)]
3#![allow(clippy::result_large_err)]
4// TODO: resolve this ^^
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7use std::{
8    borrow::Cow,
9    collections::{btree_map::IntoIter, BTreeMap},
10    fmt,
11    ops::{Deref, DerefMut},
12};
13
14pub mod error;
15#[cfg(feature = "serde")]
16#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
17mod serde;
18pub mod text;
19
20/// `pest` re-exported for your convenience :)
21pub use pest;
22
23/// Parse a KeyValues document to a loosely typed representation
24///
25/// This is shorthand for parsing a document with default settings aka `Parser::new().parse(text)`
26pub fn parse<'text>(text: &'text str) -> error::Result<PartialVdf<'text>> {
27    Parser::new().parse(text)
28}
29
30/// A configurable KeyValues parser allowing for adjusting settings before parsing
31#[derive(Clone, Debug, Default)]
32pub struct Parser {
33    literal_special_chars: bool,
34}
35
36impl Parser {
37    /// Constructs a default parser
38    ///
39    /// Currently this consists of:
40    ///
41    /// | Toggle | Description |
42    /// | :---: | :--- |
43    /// | [`Parser::literal_special_chars()`] | Whether to interpret `\` in strings as the start of an escaped special character, or a literal `\` |
44    pub const fn new() -> Self {
45        // same as Default, but const 😏
46        Self {
47            literal_special_chars: false,
48        }
49    }
50
51    /// Toggle how to interpret `\` in strings
52    ///
53    /// By default (`false`) the parser will interpret backslashes (`\`) in strings as the start of
54    /// an escaped special character (e.g. `\\` -> `\`, `\"` -> `"`). When `true` the parser will
55    /// instead interpret backslashes (`\`) as a literal backslash. Commonly seen with
56    /// windows-paths, for instance
57    pub const fn literal_special_chars(mut self, yes: bool) -> Self {
58        self.literal_special_chars = yes;
59        self
60    }
61
62    /// Parse a KeyValues document to a loosely typed representation
63    ///
64    /// # Example
65    ///
66    /// ```
67    /// use keyvalues_parser::Parser;
68    /// let vdf = Parser::new()
69    ///     .literal_special_chars(true)
70    ///     .parse(r"InstallDir C:\You\Later")
71    ///     .unwrap();
72    /// assert_eq!(vdf.value.unwrap_str(), r"C:\You\Later");
73    /// ```
74    pub fn parse<'text>(&self, vdf: &'text str) -> error::Result<PartialVdf<'text>> {
75        if self.literal_special_chars {
76            #[expect(deprecated)] // deprecated for thee, but not for me!
77            text::parse::raw_parse(vdf)
78        } else {
79            #[expect(deprecated)] // deprecated for thee, but not for me!
80            text::parse::escaped_parse(vdf)
81        }
82    }
83}
84
85/// A Key is simply an alias for `Cow<str>`
86pub type Key<'text> = Cow<'text, str>;
87
88/// A loosely typed representation of VDF text
89///
90/// `Vdf` is represented as a single [`Key`] mapped to a single [`Value`]
91///
92/// ## Parse
93///
94/// `Vdf`s will generally be created through the use of [`parse()`] or [`Parser::parse()`] which
95/// takes a string representing VDF text and attempts to parse it to a `Vdf` representation.
96///
97/// ## Mutate
98///
99/// From there you can manipulate/extract from the representation as desired by using the standard
100/// conventions on the internal types (plain old `BTreeMap`s, `Vec`s, and `Cow`s all the way down)
101///
102/// ## Render
103///
104/// The `Vdf` can also be rendered back to its text form through its `Display` implementation
105///
106/// ## Example
107///
108/// ```
109/// // Parse
110/// let vdf_text = r#"
111/// "Outer Key"
112/// {
113///     "Inner Key" "Inner Value"
114///     "Inner Key"
115///     {
116///     }
117/// }
118/// "#;
119/// let mut parsed = keyvalues_parser::parse(vdf_text)?;
120///
121/// // Mutate: i.e. remove the last "Inner Key" pair
122/// parsed
123///     .value
124///     .get_mut_obj()
125///     .unwrap()
126///     .get_mut("Inner Key")
127///     .unwrap()
128///     .pop();
129///
130/// // Render: prints
131/// // "Outer Key"
132/// // {
133/// //     "Inner Key" "Inner Value"
134/// // }
135/// println!("{}", parsed);
136/// # Ok::<(), keyvalues_parser::error::Error>(())
137/// ```
138#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
139pub struct Vdf<'text> {
140    pub key: Key<'text>,
141    pub value: Value<'text>,
142}
143
144impl<'text> Vdf<'text> {
145    /// Creates a [`Vdf`] using a provided key and value
146    ///
147    /// ```
148    /// use keyvalues_parser::{Vdf, Value};
149    /// use std::borrow::Cow;
150    ///
151    /// let vdf = Vdf::new(Cow::from("Much Key"), Value::Str(Cow::from("Such Wow")));
152    /// // prints
153    /// // "Much Key"  "Such Wow"
154    /// println!("{}", vdf);
155    /// ```
156    pub fn new(key: Key<'text>, value: Value<'text>) -> Self {
157        Self { key, value }
158    }
159}
160
161impl<'text> From<PartialVdf<'text>> for Vdf<'text> {
162    fn from(partial: PartialVdf<'text>) -> Self {
163        Self {
164            key: partial.key,
165            value: partial.value,
166        }
167    }
168}
169
170// TODO: Just store a `Vdf` internally?
171#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
172pub struct PartialVdf<'text> {
173    pub key: Key<'text>,
174    pub value: Value<'text>,
175    pub bases: Vec<Cow<'text, str>>,
176}
177
178// TODO: why is this type alias a thing if it's not private but the usage of it inside `Obj` is?
179type ObjInner<'text> = BTreeMap<Key<'text>, Vec<Value<'text>>>;
180type ObjInnerPair<'text> = (Key<'text>, Vec<Value<'text>>);
181
182#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
183pub struct Obj<'text>(pub ObjInner<'text>);
184
185/// A slightly customized multi-map debug impl
186///
187/// Two slight tweaks:
188///
189/// 1. Just show the inner map instead with no `Obj()` wrapping
190/// 2. We're a multi-map where most sequences only have one value. Omit the `[]` in single-value
191///    sequences
192impl fmt::Debug for Obj<'_> {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        let mut map_f = f.debug_map();
195        for (k, v) in &self.0 {
196            map_f.key(k);
197            match v.as_slice() {
198                [single] => _ = map_f.value(single),
199                _ => _ = map_f.value(v),
200            }
201        }
202        map_f.finish()
203    }
204}
205
206impl<'text> Obj<'text> {
207    /// Creates an empty object value
208    ///
209    /// Internally This is just a [`BTreeMap`] that maps [`Key`]s to a [`Vec`] of [`Value`]s
210    ///
211    /// ```
212    /// # use keyvalues_parser::{Obj, Value};
213    /// # use std::borrow::Cow;
214    /// let mut obj = Obj::new();
215    /// obj.insert(
216    ///     Cow::from("key"),
217    ///     vec![]
218    /// );
219    /// obj.insert(
220    ///     Cow::from("earlier key"),
221    ///     vec![Value::Obj(Obj::default())]
222    /// );
223    ///
224    /// // It's a b-tree map so the entries are sorted by keys
225    /// assert_eq!(
226    ///     obj.keys().collect::<Vec<_>>(),
227    ///     ["earlier key", "key"]
228    /// );
229    /// ```
230    pub fn new() -> Self {
231        Self::default()
232    }
233
234    /// Returns the inner [`BTreeMap`]
235    ///
236    /// ```
237    /// # use keyvalues_parser::{Obj, Value};
238    /// # use std::{borrow::Cow, collections::BTreeMap};
239    /// let mut obj = Obj::new();
240    /// obj.insert(Cow::from("much key"), vec![]);
241    ///
242    /// let inner: BTreeMap<_, _> = obj.into_inner();
243    /// // Prints:
244    /// // {
245    /// //     "much key": [],
246    /// // }
247    /// println!("{:#?}", inner);
248    /// ```
249    pub fn into_inner(self) -> ObjInner<'text> {
250        self.0
251    }
252
253    /// Creates an iterator that returns the [`Vdf`]s that compose the object
254    ///
255    /// This is notably different compared to just iterating over the `BTreeMap`s items because it
256    /// will emit a [`Vdf`] for each key-value pair while the actual items are key-values pairs.
257    /// This means that empty values will not emit a [`Vdf`] at all, and a pair that has multiple
258    /// entries in values will emit a [`Vdf`] for each pairing
259    ///
260    /// ```
261    /// # use keyvalues_parser::{Obj, Value, Vdf};
262    /// # use std::borrow::Cow;
263    /// let mut obj = Obj::new();
264    /// obj.insert(
265    ///     Cow::from("no values"),
266    ///     vec![]
267    /// );
268    /// obj.insert(
269    ///     Cow::from("multiple values"),
270    ///     vec![Value::Str(Cow::from("first")), Value::Str(Cow::from("second"))]
271    /// );
272    ///
273    /// let vdfs: Vec<_> = obj.into_vdfs().collect();
274    /// assert_eq!(
275    ///     vdfs,
276    ///     [
277    ///         Vdf {
278    ///             key: Cow::from("multiple values"),
279    ///             value: Value::Str(Cow::from("first"))
280    ///         },
281    ///         Vdf {
282    ///             key: Cow::from("multiple values"),
283    ///             value: Value::Str(Cow::from("second"))
284    ///         },
285    ///     ]
286    /// );
287    /// ```
288    pub fn into_vdfs(self) -> IntoVdfs<'text> {
289        IntoVdfs::new(self)
290    }
291}
292
293impl<'text> FromIterator<ObjInnerPair<'text>> for Obj<'text> {
294    fn from_iter<T: IntoIterator<Item = ObjInnerPair<'text>>>(iter: T) -> Self {
295        let mut inner = BTreeMap::new();
296        for (key, values) in iter {
297            inner.insert(key, values);
298        }
299
300        Self(inner)
301    }
302}
303
304impl<'text> Deref for Obj<'text> {
305    type Target = ObjInner<'text>;
306
307    fn deref(&self) -> &Self::Target {
308        &self.0
309    }
310}
311
312impl DerefMut for Obj<'_> {
313    fn deref_mut(&mut self) -> &mut Self::Target {
314        &mut self.0
315    }
316}
317
318/// An iterator over an [`Obj`]'s [`Vdf`] pairs
319///
320/// Typically created by calling [`Obj::into_vdfs`] on an existing object
321pub struct IntoVdfs<'text> {
322    // TODO: can this just store an iterator for the values instead of `.collect()`ing
323    current_entry: Option<ObjInnerPair<'text>>,
324    it: IntoIter<Key<'text>, Vec<Value<'text>>>,
325}
326
327impl<'text> IntoVdfs<'text> {
328    fn new(obj: Obj<'text>) -> Self {
329        Self {
330            current_entry: None,
331            it: obj.into_inner().into_iter(),
332        }
333    }
334}
335
336impl<'text> Iterator for IntoVdfs<'text> {
337    type Item = Vdf<'text>;
338
339    fn next(&mut self) -> Option<Self::Item> {
340        // Iteration will pop the first pair off `current_entry` if it's set and then falls back to
341        // reading in a new `current_entry` from `it`. If `it` is exhausted then we're done
342        loop {
343            match self.current_entry.take() {
344                // There is a pair to return
345                Some((key, mut values)) if !values.is_empty() => {
346                    let value = values.pop().expect("values isn't empty");
347                    self.current_entry = Some((key.clone(), values));
348                    return Some(Vdf::new(key, value));
349                }
350                _ => {
351                    let (key, values) = self.it.next()?;
352                    // Store the next entry. Flip the values so that `pop`ing returns correct order
353                    self.current_entry = Some((key, values.into_iter().rev().collect()));
354                }
355            }
356        }
357    }
358}
359
360// TODO: custom Debug that's more succinct
361/// Enum representing all valid VDF values
362///
363/// VDF is composed of [`Key`]s and their respective [`Value`]s where this represents the latter. A
364/// value is either going to be a `Str(Cow<str>)`, or an `Obj(Obj)` that contains a list of keys
365/// and values.
366///
367/// ```
368/// # use keyvalues_parser::{Obj, Value};
369/// # use std::borrow::Cow;
370/// let value_str = Value::Str(Cow::from("some text"));
371/// let value_obj = Value::Obj(Obj::new());
372/// ```
373#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
374pub enum Value<'text> {
375    Str(Cow<'text, str>),
376    Obj(Obj<'text>),
377}
378
379impl fmt::Debug for Value<'_> {
380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
381        match self {
382            Self::Str(s) => {
383                f.write_str("Str(")?;
384                fmt::Debug::fmt(s, f)?;
385                f.write_str(")")
386            }
387            Self::Obj(o) => {
388                f.write_str("Obj(")?;
389                fmt::Debug::fmt(o, f)?;
390                f.write_str(")")
391            }
392        }
393    }
394}
395
396impl<'text> Value<'text> {
397    /// Returns if the current value is the `Str` variant
398    ///
399    /// ```
400    /// use std::borrow::Cow;
401    /// use keyvalues_parser::{Obj, Value};
402    ///
403    /// let value_str = Value::Str(Cow::default());
404    /// assert!(value_str.is_str());
405    /// ```
406    pub fn is_str(&self) -> bool {
407        self.get_str().is_some()
408    }
409
410    /// Returns if the current value is the `Obj` variant
411    ///
412    /// ```
413    /// use keyvalues_parser::{Obj, Value};
414    ///
415    /// let value_obj = Value::Obj(Obj::default());
416    /// assert!(value_obj.is_obj());
417    /// ```
418    pub fn is_obj(&self) -> bool {
419        self.get_obj().is_some()
420    }
421
422    /// Gets the inner `&str` if this is a `Value::Str`
423    ///
424    /// ```
425    /// # use keyvalues_parser::Value;
426    /// # use std::borrow::Cow;
427    /// let value = Value::Str(Cow::from("some text"));
428    ///
429    /// if let Some(s) = value.get_str() {
430    ///     println!("value str: {}", s);
431    /// }
432    /// ```
433    pub fn get_str(&self) -> Option<&str> {
434        if let Self::Str(s) = self {
435            Some(s)
436        } else {
437            None
438        }
439    }
440
441    /// Gets the inner `&Obj` if this value is a `Value::Obj`
442    ///
443    /// ```
444    /// # use keyvalues_parser::{Obj, Value};
445    /// let value = Value::Obj(Obj::new());
446    ///
447    /// if let Some(obj) = value.get_obj() {
448    ///     println!("value obj: {:?}", obj);
449    /// }
450    /// ```
451    pub fn get_obj(&self) -> Option<&Obj<'_>> {
452        if let Self::Obj(obj) = self {
453            Some(obj)
454        } else {
455            None
456        }
457    }
458
459    /// Gets the inner `&mut str` if this is a `Value::Str`
460    ///
461    /// ```
462    /// # use keyvalues_parser::Value;
463    /// # use std::borrow::Cow;
464    /// let mut value = Value::Str(Cow::from("some text"));
465    /// let mut inner_str = value.get_mut_str().unwrap();
466    /// inner_str.to_mut().make_ascii_uppercase();
467    ///
468    /// assert_eq!(
469    ///     value,
470    ///     Value::Str(Cow::from("SOME TEXT"))
471    /// );
472    /// ```
473    pub fn get_mut_str(&mut self) -> Option<&mut Cow<'text, str>> {
474        if let Self::Str(s) = self {
475            Some(s)
476        } else {
477            None
478        }
479    }
480
481    /// Gets the inner `&mut Obj` if this is a `Value::Obj`
482    ///
483    /// ```
484    /// # use keyvalues_parser::{Obj, Value};
485    /// # use std::borrow::Cow;
486    /// let mut value = Value::Obj(Obj::new());
487    /// let mut inner_obj = value.get_mut_obj().unwrap();
488    /// inner_obj.insert(Cow::from("new key"), vec![]);
489    ///
490    /// // Prints:
491    /// // Value::Obj({
492    /// //    "new key": [],
493    /// // })
494    /// println!("{:?}", value);
495    /// ```
496    pub fn get_mut_obj(&mut self) -> Option<&mut Obj<'text>> {
497        if let Self::Obj(obj) = self {
498            Some(obj)
499        } else {
500            None
501        }
502    }
503
504    /// Unwraps the `Cow<str>` from the `Value::Str`
505    ///
506    /// # Panics
507    ///
508    /// If the variant was `Value::Obj`
509    ///
510    /// # Examples
511    ///
512    /// ```
513    /// use keyvalues_parser::Value;
514    /// use std::borrow::Cow;
515    ///
516    /// let value = Value::Str(Cow::from("Sample text"));
517    /// assert_eq!(value.unwrap_str(), "Sample text");
518    /// ```
519    ///
520    /// ```should_panic
521    /// use keyvalues_parser::{Value, Obj};
522    ///
523    /// let value = Value::Obj(Obj::new());
524    /// value.unwrap_str(); // <-- panics
525    /// ```
526    pub fn unwrap_str(self) -> Cow<'text, str> {
527        self.expect_str("Called `unwrap_str` on a `Value::Obj` variant")
528    }
529
530    /// Unwraps the [`Obj`] from the `Value::Obj`
531    ///
532    /// # Panics
533    ///
534    /// If the variant was `Value::Str`
535    ///
536    /// # Examples
537    ///
538    /// ```
539    /// use keyvalues_parser::{Obj, Value};
540    ///
541    /// let value = Value::Obj(Obj::new());
542    /// assert_eq!(value.unwrap_obj(), Obj::new());
543    /// ```
544    ///
545    /// ```should_panic
546    /// use keyvalues_parser::Value;
547    /// use std::borrow::Cow;
548    ///
549    /// let value = Value::Str(Cow::from("D'Oh"));
550    /// value.unwrap_obj(); // <-- panics
551    /// ```
552    pub fn unwrap_obj(self) -> Obj<'text> {
553        self.expect_obj("Called `unwrap_obj` on a `Value::Str` variant")
554    }
555
556    /// Refer to [Value::unwrap_str]. Same situation, but with a custom message
557    pub fn expect_str(self, msg: &str) -> Cow<'text, str> {
558        if let Self::Str(s) = self {
559            s
560        } else {
561            panic!("{}", msg)
562        }
563    }
564
565    /// Refer to [Value::unwrap_obj]. Same situation, but with a custom message
566    pub fn expect_obj(self, msg: &str) -> Obj<'text> {
567        if let Self::Obj(obj) = self {
568            obj
569        } else {
570            panic!("{}", msg)
571        }
572    }
573}