Skip to main content

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    /// Converts this [`Vdf`] into a fully owned variant
161    ///
162    /// Internally a [`Vdf`] can reference the underlying text. This changes all of those
163    /// references to be be allocated instead, allowing for easier ownership
164    ///
165    /// ```
166    /// fn expect_owned_vdf(text: &'_ str) -> keyvalues_parser::Vdf<'static> {
167    ///     keyvalues_parser::parse(text).unwrap().into_vdf().into_owned()
168    /// }
169    ///
170    /// let vdf = expect_owned_vdf(
171    ///     r#"foo { bar {} bar "allocates for ownership" }"#
172    /// );
173    /// assert_eq!(
174    ///     vdf.value.unwrap_obj().get("bar").unwrap()[1].get_str().unwrap(),
175    ///     "allocates for ownership",
176    /// );
177    /// ```
178    pub fn into_owned(self) -> Vdf<'static> {
179        let Self { key, value } = self;
180        let key = owned_cow(key);
181        let value = value.into_owned();
182        Vdf { key, value }
183    }
184}
185
186impl<'text> From<PartialVdf<'text>> for Vdf<'text> {
187    fn from(partial: PartialVdf<'text>) -> Self {
188        partial.into_vdf()
189    }
190}
191
192/// A top-level VDF pair containing any top level `#base` directives as well
193///
194/// Obtained by calling [`parse()`] or [`Parser::parse()`]. See [`Vdf`] for the more broad
195/// docs/API
196// TODO: Just store a `Vdf` internally
197#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
198pub struct PartialVdf<'text> {
199    pub key: Key<'text>,
200    pub value: Value<'text>,
201    pub bases: Vec<Cow<'text, str>>,
202}
203
204impl<'text> PartialVdf<'text> {
205    /// Convert a top-level VDF pair into a [`Vdf`], ignoring any `#base` directives
206    ///
207    /// This is equivalent to calling [`Vdf::from<PartialVdf>()`] or `.into()`
208    pub fn into_vdf(self) -> Vdf<'text> {
209        let Self {
210            key,
211            value,
212            bases: _,
213        } = self;
214        Vdf { key, value }
215    }
216
217    /// Converts this [`PartialVdf`] into a fully owned variant
218    ///
219    /// Internally a [`PartialVdf`] can reference the underlying text. This changes all of those
220    /// references to be be allocated instead, allowing for easier ownership
221    pub fn into_owned(self) -> PartialVdf<'static> {
222        let Self { bases, key, value } = self;
223        let bases = bases.into_iter().map(owned_cow).collect();
224        let key = owned_cow(key);
225        let value = value.into_owned();
226        PartialVdf { bases, key, value }
227    }
228}
229
230// TODO: why is this type alias a thing if it's not private but the usage of it inside `Obj` is?
231type ObjInner<'text> = BTreeMap<Key<'text>, Vec<Value<'text>>>;
232type ObjInnerPair<'text> = (Key<'text>, Vec<Value<'text>>);
233
234#[derive(Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
235pub struct Obj<'text>(pub ObjInner<'text>);
236
237/// A slightly customized multi-map debug impl
238///
239/// Two slight tweaks:
240///
241/// 1. Just show the inner map instead with no `Obj()` wrapping
242/// 2. We're a multi-map where most sequences only have one value. Omit the `[]` in single-value
243///    sequences
244impl fmt::Debug for Obj<'_> {
245    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246        let mut map_f = f.debug_map();
247        for (k, v) in &self.0 {
248            map_f.key(k);
249            match v.as_slice() {
250                [single] => _ = map_f.value(single),
251                _ => _ = map_f.value(v),
252            }
253        }
254        map_f.finish()
255    }
256}
257
258impl<'text> Obj<'text> {
259    /// Creates an empty object value
260    ///
261    /// Internally This is just a [`BTreeMap`] that maps [`Key`]s to a [`Vec`] of [`Value`]s
262    ///
263    /// ```
264    /// # use keyvalues_parser::{Obj, Value};
265    /// # use std::borrow::Cow;
266    /// let mut obj = Obj::new();
267    /// obj.insert(
268    ///     Cow::from("key"),
269    ///     vec![]
270    /// );
271    /// obj.insert(
272    ///     Cow::from("earlier key"),
273    ///     vec![Value::Obj(Obj::default())]
274    /// );
275    ///
276    /// // It's a b-tree map so the entries are sorted by keys
277    /// assert_eq!(
278    ///     obj.keys().collect::<Vec<_>>(),
279    ///     ["earlier key", "key"]
280    /// );
281    /// ```
282    pub fn new() -> Self {
283        Self::default()
284    }
285
286    /// Returns the inner [`BTreeMap`]
287    ///
288    /// ```
289    /// # use keyvalues_parser::{Obj, Value};
290    /// # use std::{borrow::Cow, collections::BTreeMap};
291    /// let mut obj = Obj::new();
292    /// obj.insert(Cow::from("much key"), vec![]);
293    ///
294    /// let inner: BTreeMap<_, _> = obj.into_inner();
295    /// // Prints:
296    /// // {
297    /// //     "much key": [],
298    /// // }
299    /// println!("{:#?}", inner);
300    /// ```
301    pub fn into_inner(self) -> ObjInner<'text> {
302        self.0
303    }
304
305    /// Converts this [`Obj`] into a fully owned variant
306    ///
307    /// Internally a [`Obj`] can reference the underlying text. This changes all of those
308    /// references to be be allocated instead, allowing for easier ownership
309    pub fn into_owned(self) -> Obj<'static> {
310        let Self(obj) = self;
311        Obj(obj
312            .into_iter()
313            .map(|(k, v)| (owned_cow(k), v.into_iter().map(Value::into_owned).collect()))
314            .collect())
315    }
316
317    /// Creates an iterator that returns the [`Vdf`]s that compose the object
318    ///
319    /// This is notably different compared to just iterating over the `BTreeMap`s items because it
320    /// will emit a [`Vdf`] for each key-value pair while the actual items are key-values pairs.
321    /// This means that empty values will not emit a [`Vdf`] at all, and a pair that has multiple
322    /// entries in values will emit a [`Vdf`] for each pairing
323    ///
324    /// ```
325    /// # use keyvalues_parser::{Obj, Value, Vdf};
326    /// # use std::borrow::Cow;
327    /// let mut obj = Obj::new();
328    /// obj.insert(
329    ///     Cow::from("no values"),
330    ///     vec![]
331    /// );
332    /// obj.insert(
333    ///     Cow::from("multiple values"),
334    ///     vec![Value::Str(Cow::from("first")), Value::Str(Cow::from("second"))]
335    /// );
336    ///
337    /// let vdfs: Vec<_> = obj.into_vdfs().collect();
338    /// assert_eq!(
339    ///     vdfs,
340    ///     [
341    ///         Vdf {
342    ///             key: Cow::from("multiple values"),
343    ///             value: Value::Str(Cow::from("first"))
344    ///         },
345    ///         Vdf {
346    ///             key: Cow::from("multiple values"),
347    ///             value: Value::Str(Cow::from("second"))
348    ///         },
349    ///     ]
350    /// );
351    /// ```
352    pub fn into_vdfs(self) -> IntoVdfs<'text> {
353        IntoVdfs::new(self)
354    }
355}
356
357impl<'text> FromIterator<ObjInnerPair<'text>> for Obj<'text> {
358    fn from_iter<T: IntoIterator<Item = ObjInnerPair<'text>>>(iter: T) -> Self {
359        let mut inner = BTreeMap::new();
360        for (key, values) in iter {
361            inner.insert(key, values);
362        }
363
364        Self(inner)
365    }
366}
367
368impl<'text> Deref for Obj<'text> {
369    type Target = ObjInner<'text>;
370
371    fn deref(&self) -> &Self::Target {
372        &self.0
373    }
374}
375
376impl DerefMut for Obj<'_> {
377    fn deref_mut(&mut self) -> &mut Self::Target {
378        &mut self.0
379    }
380}
381
382/// An iterator over an [`Obj`]'s [`Vdf`] pairs
383///
384/// Typically created by calling [`Obj::into_vdfs`] on an existing object
385pub struct IntoVdfs<'text> {
386    // TODO: can this just store an iterator for the values instead of `.collect()`ing
387    current_entry: Option<ObjInnerPair<'text>>,
388    it: IntoIter<Key<'text>, Vec<Value<'text>>>,
389}
390
391impl<'text> IntoVdfs<'text> {
392    fn new(obj: Obj<'text>) -> Self {
393        Self {
394            current_entry: None,
395            it: obj.into_inner().into_iter(),
396        }
397    }
398}
399
400impl<'text> Iterator for IntoVdfs<'text> {
401    type Item = Vdf<'text>;
402
403    fn next(&mut self) -> Option<Self::Item> {
404        // Iteration will pop the first pair off `current_entry` if it's set and then falls back to
405        // reading in a new `current_entry` from `it`. If `it` is exhausted then we're done
406        loop {
407            match self.current_entry.take() {
408                // There is a pair to return
409                Some((key, mut values)) if !values.is_empty() => {
410                    let value = values.pop().expect("values isn't empty");
411                    self.current_entry = Some((key.clone(), values));
412                    return Some(Vdf::new(key, value));
413                }
414                _ => {
415                    let (key, values) = self.it.next()?;
416                    // Store the next entry. Flip the values so that `pop`ing returns correct order
417                    self.current_entry = Some((key, values.into_iter().rev().collect()));
418                }
419            }
420        }
421    }
422}
423
424// TODO: custom Debug that's more succinct
425/// Enum representing all valid VDF values
426///
427/// VDF is composed of [`Key`]s and their respective [`Value`]s where this represents the latter. A
428/// value is either going to be a `Str(Cow<str>)`, or an `Obj(Obj)` that contains a list of keys
429/// and values.
430///
431/// ```
432/// # use keyvalues_parser::{Obj, Value};
433/// # use std::borrow::Cow;
434/// let value_str = Value::Str(Cow::from("some text"));
435/// let value_obj = Value::Obj(Obj::new());
436/// ```
437#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
438pub enum Value<'text> {
439    Str(Cow<'text, str>),
440    Obj(Obj<'text>),
441}
442
443impl<'text> Value<'text> {
444    /// Converts this [`Vdf`] into a fully owned variant
445    ///
446    /// Internally a [`Vdf`] can reference the underlying text. This changes all of those
447    /// references to be be allocated instead, allowing for easier ownership
448    pub fn into_owned(self) -> Value<'static> {
449        match self {
450            Self::Str(s) => Value::Str(owned_cow(s)),
451            Self::Obj(o) => Value::Obj(o.into_owned()),
452        }
453    }
454}
455
456impl fmt::Debug for Value<'_> {
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        match self {
459            Self::Str(s) => {
460                f.write_str("Str(")?;
461                fmt::Debug::fmt(s, f)?;
462                f.write_str(")")
463            }
464            Self::Obj(o) => {
465                f.write_str("Obj(")?;
466                fmt::Debug::fmt(o, f)?;
467                f.write_str(")")
468            }
469        }
470    }
471}
472
473impl<'text> Value<'text> {
474    /// Returns if the current value is the `Str` variant
475    ///
476    /// ```
477    /// use std::borrow::Cow;
478    /// use keyvalues_parser::{Obj, Value};
479    ///
480    /// let value_str = Value::Str(Cow::default());
481    /// assert!(value_str.is_str());
482    /// ```
483    pub fn is_str(&self) -> bool {
484        self.get_str().is_some()
485    }
486
487    /// Returns if the current value is the `Obj` variant
488    ///
489    /// ```
490    /// use keyvalues_parser::{Obj, Value};
491    ///
492    /// let value_obj = Value::Obj(Obj::default());
493    /// assert!(value_obj.is_obj());
494    /// ```
495    pub fn is_obj(&self) -> bool {
496        self.get_obj().is_some()
497    }
498
499    /// Gets the inner `&str` if this is a `Value::Str`
500    ///
501    /// ```
502    /// # use keyvalues_parser::Value;
503    /// # use std::borrow::Cow;
504    /// let value = Value::Str(Cow::from("some text"));
505    ///
506    /// if let Some(s) = value.get_str() {
507    ///     println!("value str: {}", s);
508    /// }
509    /// ```
510    pub fn get_str(&self) -> Option<&str> {
511        if let Self::Str(s) = self {
512            Some(s)
513        } else {
514            None
515        }
516    }
517
518    /// Gets the inner `&Obj` if this value is a `Value::Obj`
519    ///
520    /// ```
521    /// # use keyvalues_parser::{Obj, Value};
522    /// let value = Value::Obj(Obj::new());
523    ///
524    /// if let Some(obj) = value.get_obj() {
525    ///     println!("value obj: {:?}", obj);
526    /// }
527    /// ```
528    pub fn get_obj(&self) -> Option<&Obj<'_>> {
529        if let Self::Obj(obj) = self {
530            Some(obj)
531        } else {
532            None
533        }
534    }
535
536    /// Gets the inner `&mut str` if this is a `Value::Str`
537    ///
538    /// ```
539    /// # use keyvalues_parser::Value;
540    /// # use std::borrow::Cow;
541    /// let mut value = Value::Str(Cow::from("some text"));
542    /// let mut inner_str = value.get_mut_str().unwrap();
543    /// inner_str.to_mut().make_ascii_uppercase();
544    ///
545    /// assert_eq!(
546    ///     value,
547    ///     Value::Str(Cow::from("SOME TEXT"))
548    /// );
549    /// ```
550    pub fn get_mut_str(&mut self) -> Option<&mut Cow<'text, str>> {
551        if let Self::Str(s) = self {
552            Some(s)
553        } else {
554            None
555        }
556    }
557
558    /// Gets the inner `&mut Obj` if this is a `Value::Obj`
559    ///
560    /// ```
561    /// # use keyvalues_parser::{Obj, Value};
562    /// # use std::borrow::Cow;
563    /// let mut value = Value::Obj(Obj::new());
564    /// let mut inner_obj = value.get_mut_obj().unwrap();
565    /// inner_obj.insert(Cow::from("new key"), vec![]);
566    ///
567    /// // Prints:
568    /// // Value::Obj({
569    /// //    "new key": [],
570    /// // })
571    /// println!("{:?}", value);
572    /// ```
573    pub fn get_mut_obj(&mut self) -> Option<&mut Obj<'text>> {
574        if let Self::Obj(obj) = self {
575            Some(obj)
576        } else {
577            None
578        }
579    }
580
581    /// Unwraps the `Cow<str>` from the `Value::Str`
582    ///
583    /// # Panics
584    ///
585    /// If the variant was `Value::Obj`
586    ///
587    /// # Examples
588    ///
589    /// ```
590    /// use keyvalues_parser::Value;
591    /// use std::borrow::Cow;
592    ///
593    /// let value = Value::Str(Cow::from("Sample text"));
594    /// assert_eq!(value.unwrap_str(), "Sample text");
595    /// ```
596    ///
597    /// ```should_panic
598    /// use keyvalues_parser::{Value, Obj};
599    ///
600    /// let value = Value::Obj(Obj::new());
601    /// value.unwrap_str(); // <-- panics
602    /// ```
603    pub fn unwrap_str(self) -> Cow<'text, str> {
604        self.expect_str("Called `unwrap_str` on a `Value::Obj` variant")
605    }
606
607    /// Unwraps the [`Obj`] from the `Value::Obj`
608    ///
609    /// # Panics
610    ///
611    /// If the variant was `Value::Str`
612    ///
613    /// # Examples
614    ///
615    /// ```
616    /// use keyvalues_parser::{Obj, Value};
617    ///
618    /// let value = Value::Obj(Obj::new());
619    /// assert_eq!(value.unwrap_obj(), Obj::new());
620    /// ```
621    ///
622    /// ```should_panic
623    /// use keyvalues_parser::Value;
624    /// use std::borrow::Cow;
625    ///
626    /// let value = Value::Str(Cow::from("D'Oh"));
627    /// value.unwrap_obj(); // <-- panics
628    /// ```
629    pub fn unwrap_obj(self) -> Obj<'text> {
630        self.expect_obj("Called `unwrap_obj` on a `Value::Str` variant")
631    }
632
633    /// Refer to [Value::unwrap_str]. Same situation, but with a custom message
634    pub fn expect_str(self, msg: &str) -> Cow<'text, str> {
635        if let Self::Str(s) = self {
636            s
637        } else {
638            panic!("{}", msg)
639        }
640    }
641
642    /// Refer to [Value::unwrap_obj]. Same situation, but with a custom message
643    pub fn expect_obj(self, msg: &str) -> Obj<'text> {
644        if let Self::Obj(obj) = self {
645            obj
646        } else {
647            panic!("{}", msg)
648        }
649    }
650}
651
652fn owned_cow(s: Cow<'_, str>) -> Cow<'static, str> {
653    Cow::Owned(s.into_owned())
654}