keyvalues_parser/text/
parse.rs1#![allow(renamed_and_removed_lints)]
3#![allow(clippy::unknown_clippy_lints)]
4#![allow(clippy::upper_case_acronyms)]
5
6use pest::{iterators::Pair as PestPair, Parser};
7use pest_derive::Parser;
8
9use std::borrow::Cow;
10
11use crate::{error::Result, Obj, PartialVdf as Vdf, Value};
12
13macro_rules! common_parsing {
14 ($parser:ty, $rule:ty, $parse_escaped:expr) => {
15 pub fn parse(s: &str) -> Result<Vdf<'_>> {
17 let mut full_grammar = <$parser>::parse(<$rule>::vdf, s)?;
18
19 let mut bases = Vec::new();
21 loop {
22 let pair = full_grammar.next().unwrap();
23 if let <$rule>::base_macro = pair.as_rule() {
24 let base_path_string = pair.into_inner().next().unwrap();
25 let base_path = match base_path_string.as_rule() {
26 <$rule>::quoted_raw_string => base_path_string.into_inner().next().unwrap(),
27 <$rule>::unquoted_string => base_path_string,
28 _ => unreachable!("Prevented by grammar"),
29 }
30 .as_str();
31 bases.push(Cow::from(base_path));
32 } else {
33 let (key, value) = parse_pair(pair);
34 return Ok(Vdf { key, value, bases });
35 }
36 }
37 }
38
39 fn parse_pair(grammar_pair: PestPair<'_, $rule>) -> (Cow<'_, str>, Value<'_>) {
47 if let <$rule>::pair = grammar_pair.as_rule() {
51 let mut grammar_pair_innards = grammar_pair.into_inner();
53 let grammar_string = grammar_pair_innards.next().unwrap();
54 let key = parse_string(grammar_string);
55
56 let grammar_value = grammar_pair_innards.next().unwrap();
57 let value = Value::from(grammar_value);
58
59 (key, value)
60 } else {
61 unreachable!("Prevented by grammar");
62 }
63 }
64
65 fn parse_string(grammar_string: PestPair<'_, $rule>) -> Cow<'_, str> {
66 match grammar_string.as_rule() {
67 <$rule>::quoted_string => {
72 let quoted_inner = grammar_string.into_inner().next().unwrap();
73 if $parse_escaped {
74 parse_escaped_string(quoted_inner)
75 } else {
76 Cow::from(quoted_inner.as_str())
77 }
78 }
79 <$rule>::unquoted_string => {
81 let s = grammar_string.as_str();
82 Cow::from(s)
83 }
84 _ => unreachable!("Prevented by grammar"),
85 }
86 }
87
88 fn parse_escaped_string(inner: PestPair<'_, $rule>) -> Cow<'_, str> {
92 let s = inner.as_str();
93
94 if s.contains('\\') {
95 let mut escaped = String::with_capacity(s.len());
97 let mut it = s.chars();
98
99 while let Some(ch) = it.next() {
100 if ch == '\\' {
101 match it.next() {
104 Some('n') => escaped.push('\n'),
105 Some('r') => escaped.push('\r'),
106 Some('t') => escaped.push('\t'),
107 Some('\\') => escaped.push('\\'),
108 Some('\"') => escaped.push('\"'),
109 _ => unreachable!("Prevented by grammar"),
110 }
111 } else {
112 escaped.push(ch)
113 }
114 }
115
116 Cow::from(escaped)
117 } else {
118 Cow::from(s)
119 }
120 }
121
122 impl<'a> From<PestPair<'a, $rule>> for Value<'a> {
123 fn from(grammar_value: PestPair<'a, $rule>) -> Self {
124 match grammar_value.as_rule() {
126 <$rule>::quoted_string | <$rule>::unquoted_string => {
128 Self::Str(parse_string(grammar_value))
129 }
130 <$rule>::obj => {
133 let mut obj = Obj::new();
134 for grammar_pair in grammar_value.into_inner() {
135 let (key, value) = parse_pair(grammar_pair);
136 let entry = obj.entry(key).or_default();
137 (*entry).push(value);
138 }
139
140 Self::Obj(obj)
141 }
142 _ => unreachable!("Prevented by grammar"),
143 }
144 }
145 }
146 };
147}
148
149pub use escaped::{parse as escaped_parse, PestError as EscapedPestError};
150pub use raw::{parse as raw_parse, PestError as RawPestError};
151
152impl<'a> Vdf<'a> {
153 pub fn parse(s: &'a str) -> Result<Self> {
155 escaped_parse(s)
156 }
157
158 pub fn parse_raw(s: &'a str) -> Result<Self> {
159 raw_parse(s)
160 }
161}
162
163impl<'a> crate::Vdf<'a> {
164 pub fn parse(s: &'a str) -> Result<Self> {
166 Ok(crate::Vdf::from(Vdf::parse(s)?))
167 }
168
169 pub fn parse_raw(s: &'a str) -> Result<Self> {
170 Ok(crate::Vdf::from(Vdf::parse_raw(s)?))
171 }
172}
173
174mod escaped {
175 use super::*;
176
177 #[derive(Parser)]
178 #[grammar = "grammars/escaped.pest"]
179 struct EscapedParser;
180
181 pub type PestError = pest::error::Error<Rule>;
182
183 common_parsing!(EscapedParser, Rule, true);
184}
185
186mod raw {
187 use super::*;
188
189 #[derive(Parser)]
190 #[grammar = "grammars/raw.pest"]
191 struct RawParser;
192
193 pub type PestError = pest::error::Error<Rule>;
194
195 common_parsing!(RawParser, Rule, false);
196}