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}