1use pest::Parser;
2use pest::error::Error;
3use pest::iterators::Pair;
4use pest_derive::Parser;
5
6use crate::{Header, Metadata, ObjectKey, Value};
7
8#[derive(Parser)]
9#[grammar = "grammar.pest"]
10struct KV3Parser;
11
12pub fn from_str(str: &str) -> Result<Value, Error<Rule>> {
13 let file = KV3Parser::parse(Rule::file, str)?.next().unwrap();
14
15 fn parse_value(pair: Pair<Rule>) -> Value {
16 match pair.as_rule() {
17 Rule::file => {
18 let mut inner = pair.into_inner();
19
20 let next = inner.next().unwrap();
21
22 if next.as_rule() == Rule::header {
23 let metadata = next
24 .into_inner()
25 .map(|pair| {
26 let mut inner = pair.into_inner();
27
28 let key = inner.next().unwrap().as_str().to_owned();
29 let value = inner.next().unwrap().as_str().to_owned();
30 let version = inner.next().unwrap().as_str().to_owned();
31
32 Metadata {
33 key,
34 value,
35 version,
36 }
37 })
38 .collect::<Vec<_>>();
39 let header = Header(metadata);
40 let root = Box::new(parse_value(inner.next().unwrap()));
41 Value::File(header, root)
42 } else {
43 parse_value(next)
44 }
45 }
46 Rule::object => Value::Object(
47 pair.into_inner()
48 .map(|pair| {
49 let mut inner = pair.into_inner();
50
51 let key = inner.next().unwrap();
52 let key = match key.as_rule() {
53 Rule::identifier => ObjectKey::Identifier(key.as_str().to_owned()),
54 Rule::string => ObjectKey::String(
55 key.into_inner().next().unwrap().as_str().to_owned(),
56 ),
57 _ => unreachable!(),
58 };
59
60 let value = parse_value(inner.next().unwrap());
61
62 (key, value)
63 })
64 .collect(),
65 ),
66 Rule::array => Value::Array(pair.into_inner().map(parse_value).collect()),
67 Rule::flag => {
68 let mut inner = pair.into_inner();
69 let key = inner.next().unwrap().as_str().to_owned();
70 let value = parse_value(inner.next().unwrap());
71 Value::Flag(key, Box::new(value))
72 }
73 Rule::string => Value::String(pair.into_inner().next().unwrap().as_str().to_owned()),
74 Rule::multiline_string => Value::MultilineString(pair.into_inner().next().unwrap().as_str().to_owned()),
75 Rule::number => Value::Number(pair.as_str().parse().unwrap()),
76 Rule::boolean => Value::Bool(pair.as_str().parse().unwrap()),
77 Rule::null => Value::Null,
78 _ => unreachable!(),
79 }
80 }
81
82 Ok(parse_value(file))
83}