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}