1use std::fmt::Display;
2
3use super::{
4 string::{
5 DoubleQuotedStringEscapedChar, DoubleQuotedStringPart, SingleQuotedStringEscapedChar,
6 SingleQuotedStringPart,
7 },
8 AliasedYaml, ArrayData, BlockChomping, Document, DocumentData, HashData, HashElement, Yaml,
9};
10use pest_consume::{match_nodes, Error, Parser};
11
12#[derive(Debug)]
13pub struct YamlError(Error<Rule>);
14
15impl From<Error<Rule>> for YamlError {
16 fn from(err: Error<Rule>) -> Self {
17 YamlError(err)
18 }
19}
20
21impl Display for YamlError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 write!(f, "{}", self.0)
24 }
25}
26
27impl YamlError {
28 pub fn location(&self) -> (usize, usize) {
29 match self.0.line_col {
30 pest::error::LineColLocation::Pos((line, col)) => (line, col),
31 pest::error::LineColLocation::Span((line, col), _) => (line, col),
32 }
33 }
34}
35
36pub type YamlResult<T> = std::result::Result<T, YamlError>;
37type InternalYamlResult<T> = std::result::Result<T, Error<Rule>>;
38pub type DocumentResult = YamlResult<Document>;
39type Node<'i> = pest_consume::Node<'i, Rule, ()>;
40
41#[derive(Parser)]
42#[grammar = "yaml.pest"]
43pub struct YamlParser;
44
45#[pest_consume::parser]
46impl YamlParser {
47 fn EOI(_input: Node) -> InternalYamlResult<()> {
48 Ok(())
49 }
50
51 fn comment_text(input: Node) -> InternalYamlResult<String> {
52 Ok(input.as_str().to_string())
53 }
54
55 fn comment(input: Node) -> InternalYamlResult<String> {
56 match_nodes!(input.into_children();
57 [comment_text(inner)] => Ok(inner),
58 )
59 }
60
61 fn commentnl(input: Node) -> InternalYamlResult<String> {
62 match_nodes!(input.into_children();
63 [comment_text(inner)] => Ok(inner),
64 )
65 }
66
67 fn alias(input: Node) -> InternalYamlResult<String> {
68 match_nodes!(input.into_children();
69 [alias_name(inner)] => Ok(inner),
70 )
71 }
72
73 fn alias_name(input: Node) -> InternalYamlResult<String> {
74 Ok(input.as_str().to_string())
75 }
76
77 fn anchor(input: Node) -> InternalYamlResult<Yaml> {
78 match_nodes!(input.into_children();
79 [alias_name(inner)] => Ok(Yaml::Anchor(inner)),
80 )
81 }
82
83 fn inline_hash_element(input: Node) -> InternalYamlResult<(String, Yaml)> {
84 match_nodes!(input.into_children();
85 [hash_key(k), inline_array_value(v)] => {
86
87 Ok((k, v))
88 })
89 }
90
91 fn inline_hash(input: Node) -> InternalYamlResult<Yaml> {
92 match_nodes!(input.into_children();
93 [inline_hash_element(v), inline_hash_element(vs)..] => {
94 let mut values = vec![v];
95 values.extend(vs);
96 Ok(Yaml::InlineHash(values))
97 },
98 [] => Ok(Yaml::InlineHash(vec![])))
99 }
100
101 fn unquoted_string(input: Node) -> InternalYamlResult<Yaml> {
102 Ok(Yaml::UnquotedString(
103 input.as_str().to_string().trim_end_matches(" ").to_string(),
104 ))
105 }
106
107 fn unquoted_inline_string(input: Node) -> InternalYamlResult<Yaml> {
108 Ok(Yaml::UnquotedString(
109 input.as_str().to_string().trim_end_matches(" ").to_string(),
110 ))
111 }
112
113 fn escaped_double_quote_char_value(
114 input: Node,
115 ) -> InternalYamlResult<DoubleQuotedStringEscapedChar> {
116 match input.as_str().chars().next().unwrap() {
117 'n' => Ok(DoubleQuotedStringEscapedChar::Newline),
118 '\n' => Ok(DoubleQuotedStringEscapedChar::RealNewline),
119 'r' => Ok(DoubleQuotedStringEscapedChar::CarriageReturn),
120 't' => Ok(DoubleQuotedStringEscapedChar::Tab),
121 '\\' => Ok(DoubleQuotedStringEscapedChar::Backslash),
122 '"' => Ok(DoubleQuotedStringEscapedChar::Quote),
123 _ => unreachable!(),
124 }
125 }
126
127 fn double_quote_text(input: Node) -> InternalYamlResult<String> {
128 Ok(input.as_str().to_string())
129 }
130
131 fn removable_newline(_input: Node) -> InternalYamlResult<()> {
132 Ok(())
133 }
134
135 fn blank_lines(input: Node) -> InternalYamlResult<usize> {
136 Ok(input.as_str().lines().count() - 1)
137 }
138
139 fn double_quote_without_escape(input: Node) -> InternalYamlResult<DoubleQuotedStringPart> {
140 match_nodes!(input.into_children();
141 [escaped_double_quote_char_value(inner)] => Ok(DoubleQuotedStringPart::EscapedChar(inner)),
142 [double_quote_text(inner)] => Ok(DoubleQuotedStringPart::String(inner)),
143 [blank_lines(inner)] => Ok(DoubleQuotedStringPart::BlankLines(inner)),
144 [removable_newline(_)] => Ok(DoubleQuotedStringPart::RemovableNewline),
145 )
146 }
147
148 fn double_quote_content(input: Node) -> InternalYamlResult<Vec<DoubleQuotedStringPart>> {
149 match_nodes!(input.into_children();
150 [double_quote_without_escape(v)..] => Ok(v.collect()),
151 )
152 }
153
154 fn double_quoted_string(input: Node) -> InternalYamlResult<Yaml> {
155 match_nodes!(input.into_children();
156 [double_quote_content(v)] => Ok(Yaml::DoubleQuotedString(v)),
157 )
158 }
159
160 fn single_quote(input: Node) -> InternalYamlResult<SingleQuotedStringEscapedChar> {
161 match input.as_str().chars().next().unwrap() {
162 '\'' => Ok(SingleQuotedStringEscapedChar::SingleQuote),
163 _ => unreachable!(),
164 }
165 }
166
167 fn single_quote_text(input: Node) -> InternalYamlResult<String> {
168 Ok(input.as_str().to_string())
169 }
170
171 fn escaped_single_quote_char(input: Node) -> InternalYamlResult<SingleQuotedStringPart> {
172 match_nodes!(input.into_children();
173 [single_quote(inner)] => Ok(SingleQuotedStringPart::EscapedChar(inner)),
174 [single_quote_text(inner)] => Ok(SingleQuotedStringPart::String(inner)),
175 [blank_lines(inner)] => Ok(SingleQuotedStringPart::BlankLines(inner)),
176 [removable_newline(_)] => Ok(SingleQuotedStringPart::RemovableNewline),
177 )
178 }
179
180 fn single_quote_content(input: Node) -> InternalYamlResult<Vec<SingleQuotedStringPart>> {
181 match_nodes!(input.into_children();
182 [escaped_single_quote_char(v)..] => Ok(v.collect()),
183 )
184 }
185
186 fn single_quoted_string(input: Node) -> InternalYamlResult<Yaml> {
187 match_nodes!(input.into_children();
188 [single_quote_content(v)] => Ok(Yaml::SingleQuotedString(v)),
189 )
190 }
191
192 fn inline_array_string(input: Node) -> InternalYamlResult<Yaml> {
193 match_nodes!(input.into_children();
194 [unquoted_inline_string(value)] => Ok(value),
195 [double_quoted_string(value)] => Ok(value),
196 [single_quoted_string(value)] => Ok(value))
197 }
198
199 fn inline_array_value(input: Node) -> InternalYamlResult<Yaml> {
200 match_nodes!(input.into_children();
201 [anchor(value)] => Ok(value),
202 [inline_array(value)] => Ok(value),
203 [inline_hash(value)] => Ok(value),
204 [inline_array_string(value)] => Ok(value))
205 }
206
207 fn inline_array(input: Node) -> InternalYamlResult<Yaml> {
208 match_nodes!(input.into_children();
209 [inline_array_value(v), inline_array_value(vs)..] => {
210 let mut values = vec![v];
211 values.extend(vs);
212 Ok(Yaml::InlineArray(values))
213 },
214 [] => Ok(Yaml::InlineArray(vec![])))
215 }
216
217 fn string(input: Node) -> InternalYamlResult<Yaml> {
218 match_nodes!(input.into_children();
219 [unquoted_string(value)] => Ok(value),
220 [double_quoted_string(value)] => Ok(value),
221 [single_quoted_string(value)] => Ok(value))
222 }
223
224 fn inline_value(input: Node) -> InternalYamlResult<Yaml> {
225 match_nodes!(input.into_children();
226 [anchor(value)] => Ok(value),
227 [inline_array(value)] => Ok(value),
228 [inline_hash(value)] => Ok(value),
229 [string(value)] => Ok(value))
230 }
231
232 fn block_string(input: Node) -> InternalYamlResult<String> {
233 Ok(input.as_str().to_string())
234 }
235
236 fn block_empty_lines(input: Node) -> InternalYamlResult<Vec<String>> {
237 Ok((0..input.as_str().len())
238 .into_iter()
239 .map(|_| "".to_string())
240 .collect())
241 }
242
243 fn string_block_chomping(input: Node) -> InternalYamlResult<BlockChomping> {
244 Ok(match input.as_str() {
245 "+" => BlockChomping::Keep,
246 "-" => BlockChomping::Strip,
247 _ => unreachable!(),
248 })
249 }
250
251 fn string_multiline_indent(input: Node) -> InternalYamlResult<Option<usize>> {
252 Ok(if input.as_str().is_empty() {
253 None
254 } else {
255 let given: usize = input.as_str().parse().unwrap();
256 Some(given - 1)
257 })
258 }
259
260 fn string_multiline_content_part(input: Node) -> InternalYamlResult<Vec<String>> {
261 match_nodes!(input.into_children();
262 [block_empty_lines(es), block_string(b)] => {
263 Ok(es.into_iter().chain(std::iter::once(b)).collect())
264 })
265 }
266
267 fn string_multiline_trailing(input: Node) -> InternalYamlResult<Vec<String>> {
268 Ok(input.as_str().chars().filter(|c| *c == '\n').map(|_| String::new()).collect())
269 }
270
271 fn string_multiline_content(input: Node) -> InternalYamlResult<Vec<String>> {
272 match_nodes!(input.into_children();
273 [block_empty_lines(es), block_string(b), string_multiline_content_part(bs).., string_multiline_trailing(trailing)] => {
274 Ok(es.into_iter()
275 .chain(std::iter::once(b))
276 .chain(bs.into_iter().flat_map(|x| x))
277 .chain(trailing)
278 .collect())
279 })
280 }
281
282 fn string_multiline_folded(input: Node) -> InternalYamlResult<Yaml> {
283 match_nodes!(input.into_children();
284 [string_multiline_indent(indent), string_multiline_content(cs)] => Ok(Yaml::FoldedString(
285 clean_multiline_strings(indent, cs), BlockChomping::Clip
286 )),
287 [string_block_chomping(chomp), string_multiline_indent(indent), string_multiline_content(cs)] => Ok(Yaml::FoldedString(
288 clean_multiline_strings(indent, cs), chomp
289 )))
290
291 }
293
294 fn string_multiline_literal(input: Node) -> InternalYamlResult<Yaml> {
295 match_nodes!(input.into_children();
296 [string_multiline_indent(indent), string_multiline_content(cs)] => Ok(Yaml::LiteralString(
297 clean_multiline_strings(indent, cs), BlockChomping::Clip
298 )),
299 [string_block_chomping(chomp), string_multiline_indent(indent), string_multiline_content(cs)] => Ok(Yaml::LiteralString(
300 clean_multiline_strings(indent, cs), chomp
301 )))
302 }
303
304 fn yaml_value(input: Node) -> InternalYamlResult<Yaml> {
305 match_nodes!(input.into_children();
306 [hash(value)] => Ok(value),
307 [array(value)] => Ok(value),
308 [string_multiline_literal(value)] => Ok(value),
309 [string_multiline_folded(value)] => Ok(value),
310 [inline_value(value)] => Ok(value))
311 }
312
313 fn aliased_yaml_value(input: Node) -> InternalYamlResult<AliasedYaml> {
314 match_nodes!(input.into_children();
315 [alias(alias), yaml_value(val)] => Ok(AliasedYaml {
316 alias: Some(alias),
317 value: val,
318 }),
319 [yaml_value(val)] => Ok(AliasedYaml {
320 alias: None,
321 value: val,
322 }))
323 }
324
325 fn block_array_aliased_yaml_value(input: Node) -> InternalYamlResult<AliasedYaml> {
326 match_nodes!(input.into_children();
327 [alternative_aliased_yaml_value(value)] => Ok(value),
328 [aliased_yaml_value(value)] => Ok(value))
329 }
330
331 fn alternative_aliased_yaml_value(input: Node) -> InternalYamlResult<AliasedYaml> {
332 match_nodes!(input.into_children();
333 [alias(alias), alternative_hash(val)] => Ok(AliasedYaml {
334 alias: Some(alias),
335 value: val,
336 }),
337 [alternative_hash(val)] => Ok(AliasedYaml {
338 alias: None,
339 value: val,
340 }),
341 [alias(alias), alternative_array(val)] => Ok(AliasedYaml {
342 alias: Some(alias),
343 value: val,
344 }),
345 [alternative_array(val)] => Ok(AliasedYaml {
346 alias: None,
347 value: val,
348 }))
349 }
350
351 fn hash_key(input: Node) -> InternalYamlResult<String> {
352 Ok(input.as_str().trim_end().to_string())
353 }
354
355 fn hash_element(input: Node) -> InternalYamlResult<HashElement> {
356 match_nodes!(input.into_children();
358 [hash_key(key), block_array_aliased_yaml_value(value)] => Ok(HashElement { key, value }),
359 )
360 }
361
362 fn first_hash_element(input: Node) -> InternalYamlResult<Vec<HashData>> {
363 match_nodes!(input.into_children();
366 [hash_element(element), comment(c)] => Ok(vec![HashData::Element(element), HashData::InlineComment(c)]),
367 [hash_element(element)] => Ok(vec![HashData::Element(element)]),
368
369 )
370 }
371
372 fn hash_element_data(input: Node) -> InternalYamlResult<Vec<HashData>> {
373 match_nodes!(input.into_children();
376 [commentnls(cs), hash_element(element), comment(c)] => Ok(
377 cs.into_iter().map(|c| HashData::Comment(c))
378 .chain(vec![HashData::Element(element), HashData::InlineComment(c)].into_iter())
379 .collect(),
380 ),
381 [commentnls(cs), hash_element(element)] => Ok(
382 cs.into_iter().map(|c| HashData::Comment(c))
383 .chain(vec![HashData::Element(element)].into_iter())
384 .collect(),
385 ),
386 )
387 }
388
389 fn hash(input: Node) -> InternalYamlResult<Yaml> {
390 match_nodes!(input.into_children();
392 [commentnls(cs), first_hash_element(element1), hash_element_data(elements).., commentnls(cs2)] => Ok(
393 Yaml::Hash(
394 cs.into_iter().map(|c| HashData::Comment(c))
395 .chain(element1.into_iter())
396 .chain(elements.into_iter().flatten())
397 .chain(cs2.into_iter().map(|c| HashData::Comment(c)))
398 .collect()
399 )
400 ),
401 )
402 }
403
404 fn alternative_hash_continuation(input: Node) -> InternalYamlResult<Vec<HashData>> {
405 match_nodes!(input.into_children();
406 [commentnls(cs), first_hash_element(element), hash_element_data(elements)..] => Ok(
407 cs.into_iter().map(|c| HashData::Comment(c))
408 .chain(element.into_iter())
409 .chain(elements.into_iter().flatten())
410 .collect()
411 ),
412 )
413 }
414
415 fn alternative_hash(input: Node) -> InternalYamlResult<Yaml> {
416 match_nodes!(input.into_children();
417 [first_hash_element(element1), alternative_hash_continuation(cont), commentnls(cs)] => Ok(
418 Yaml::Hash(
419 element1.into_iter()
420 .chain(cont.into_iter())
421 .chain(cs.into_iter().map(|c| HashData::Comment(c)))
422 .collect()
423 )
424 ),
425 [first_hash_element(element1), commentnls(cs)] => Ok(
426 Yaml::Hash(
427 element1.into_iter()
428 .chain(cs.into_iter().map(|c| HashData::Comment(c)))
429 .collect()
430 )
431 ),
432 )
433 }
434
435 fn block_array_element(input: Node) -> InternalYamlResult<ArrayData> {
436 match_nodes!(input.into_children();
437 [block_array_aliased_yaml_value(value)] => Ok(ArrayData::Element(value)),
438 )
439 }
440
441 fn first_block_array_element(input: Node) -> InternalYamlResult<Vec<ArrayData>> {
442 match_nodes!(input.into_children();
443 [block_array_element(element), comment(c)] => Ok(vec![element, ArrayData::InlineComment(c)]),
444 [block_array_element(element)] => Ok(vec![element]),
445 )
446 }
447
448 fn block_array_data(input: Node) -> InternalYamlResult<Vec<ArrayData>> {
449 match_nodes!(input.into_children();
450 [commentnls(cs), block_array_element(element), comment(c)] => Ok(
451 cs.into_iter().map(|c| ArrayData::Comment(c))
452 .chain(vec![element, ArrayData::InlineComment(c)].into_iter())
453 .collect(),
454 ),
455 [commentnls(cs), block_array_element(element)] => Ok(
456 cs.into_iter().map(|c| ArrayData::Comment(c))
457 .chain(vec![element].into_iter())
458 .collect(),
459 ),
460 )
461 }
462
463 fn array(input: Node) -> InternalYamlResult<Yaml> {
464 match_nodes!(input.into_children();
465 [inline_array(val)] => Ok(val),
466 [block_array(val)] => Ok(val)
467 )
468 }
469
470 fn block_array(input: Node) -> InternalYamlResult<Yaml> {
471 match_nodes!(input.into_children();
472 [commentnls(cs),first_block_array_element(element1), block_array_data(elements).., commentnls(cs2)] => Ok(
473 Yaml::Array(
474 cs.into_iter().map(|c| ArrayData::Comment(c))
475 .chain(element1.into_iter())
476 .chain(elements.into_iter().flatten())
477 .chain(cs2.into_iter().map(|c| ArrayData::Comment(c)))
478 .collect()
479 )
480 ))
481 }
482
483 fn alternative_array(input: Node) -> InternalYamlResult<Yaml> {
484 match_nodes!(input.into_children();
485 [first_block_array_element(element1),first_block_array_element(element2), block_array_data(elements).., commentnls(cs)] => Ok(
486 Yaml::Array(
487 element1.into_iter()
488 .chain(element2.into_iter())
489 .chain(elements.into_iter().flatten())
490 .chain(cs.into_iter().map(|c| ArrayData::Comment(c)))
491 .collect()
492 )
493 ))
494 }
495
496 fn commentnls(input: Node) -> InternalYamlResult<Vec<String>> {
497 match_nodes!(
499 input.into_children();
500 [commentnl(cs)..] => Ok(
501 cs.collect()
502
503 )
504 )
505 }
506
507 fn leading_comments(input: Node) -> InternalYamlResult<Vec<String>> {
508 match_nodes!(
510 input.into_children();
511 [commentnl(cs)..] => Ok(
512 cs.collect()
513
514 )
515 )
516 }
517
518 fn document(input: Node) -> InternalYamlResult<Yaml> {
519 match_nodes!(input.into_children();
520 [hash(h)] => Ok(h),
521 [array(a)] => Ok(a))
522 }
523
524 fn yaml(input: Node) -> InternalYamlResult<Document> {
525 match_nodes!(input.into_children();
527 [leading_comments(before), commentnls(cs), document(val), comment(cs2).., EOI(_)] => Ok(
528 Document {
529 leading_comments: before,
530 items: cs.into_iter().map(|c| DocumentData::Comment(c))
531 .chain(vec![DocumentData::Yaml(val)].into_iter())
532 .chain(cs2.into_iter().map(|c| DocumentData::Comment(c))).collect(),
533 }
534 ))
535 }
536}
537
538pub fn parse_yaml_file(input_str: &str) -> YamlResult<Document> {
539 let inputs = YamlParser::parse(Rule::yaml, input_str)?;
541 let input = inputs.single()?;
543 YamlParser::yaml(input).map_err(|e| e.into())
545}
546
547fn count_whitespace_bytes_at_start(input: &str) -> usize {
548 input
549 .chars()
550 .take_while(|ch| ch.is_whitespace() && *ch != '\n')
551 .map(|ch| ch.len_utf8())
552 .sum()
553}
554
555fn clean_multiline_strings(indent: Option<usize>, lines: Vec<String>) -> Vec<String> {
556 let indent = indent.unwrap_or_else(|| count_whitespace_bytes_at_start(&lines[0]));
557
558 lines
559 .into_iter()
560 .map(|x| {
561 if x.is_empty() {
562 x
563 } else {
564 x[indent..].to_string()
565 }
566 })
567 .collect()
568}