miniconf_pest_parser/
parser.rs1use pest::Parser;
2use pest_derive::Parser;
3use std::collections::HashMap;
4
5#[derive(Parser)]
6#[grammar = "miniconf.pest"]
7struct MiniConfParser;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum Value {
11 Str(String),
12 Num(f64),
13 Bool(bool),
14 Null,
15 Array(Vec<Value>),
16 Object(HashMap<String, Value>),
17}
18
19#[derive(Debug, Clone, Default, PartialEq)]
20pub struct Document {
21 pub sections: HashMap<String, HashMap<String, Value>>,
23}
24
25#[derive(thiserror::Error, Debug)]
26pub enum MiniConfError {
27 #[error("parse error: {0}")]
28 Pest(#[from] pest::error::Error<Rule>),
29 #[error("number parse error: {0}")]
30 Num(#[from] std::num::ParseFloatError),
31}
32
33impl Document {
34 pub fn parse(input: &str) -> Result<Self, MiniConfError> {
35 let mut current_section = String::from("root");
36 let mut doc = Document::default();
37 doc.sections.entry(current_section.clone()).or_default();
38
39 let pairs = MiniConfParser::parse(Rule::file, input)?;
40
41 for pair in pairs.flatten() {
42 match pair.as_rule() {
43 Rule::section => {
44 let mut inner = pair.into_inner();
45 let name = inner.next().unwrap().as_str().to_string();
46 current_section = name;
47 doc.sections.entry(current_section.clone()).or_default();
48 }
49 Rule::kv => {
50 let mut inner = pair.into_inner();
51 let key = inner.next().unwrap().as_str().to_string();
52 let val_pair = inner.next().unwrap();
53 let val = parse_value(val_pair)?;
54 doc.sections
55 .entry(current_section.clone())
56 .or_default()
57 .insert(key, val);
58 }
59 _ => {}
60 }
61 }
62 Ok(doc)
63 }
64}
65
66fn parse_value(pair: pest::iterators::Pair<Rule>) -> Result<Value, MiniConfError> {
67 Ok(match pair.as_rule() {
68 Rule::string => {
69 let raw = pair.as_str();
70 let inner = &raw[1..raw.len() - 1];
71 let s = inner
72 .replace("\\\"", "\"")
73 .replace("\\n", "\n")
74 .replace("\\t", "\t");
75 Value::Str(s)
76 }
77 Rule::number => Value::Num(pair.as_str().parse::<f64>()?),
78 Rule::boolean => Value::Bool(pair.as_str() == "true"),
79 Rule::null => Value::Null,
80 Rule::array => {
81 let items = pair
82 .into_inner()
83 .filter(|p| matches!(p.as_rule(), Rule::value))
84 .map(parse_value)
85 .collect::<Result<Vec<_>, _>>()?;
86 Value::Array(items)
87 }
88 Rule::object => {
89 let mut map = HashMap::new();
90 let mut it = pair.into_inner().peekable();
91 while let Some(next) = it.next() {
92 if next.as_rule() == Rule::ident {
93 let key = next.as_str().to_string();
94 let val_pair = it.next().expect("value after key");
95 let val = parse_value(val_pair)?;
96 map.insert(key, val);
97 }
98 }
99 Value::Object(map)
100 }
101 Rule::value => {
102 let inner = pair.into_inner().next().unwrap();
103 parse_value(inner)?
104 }
105 _ => unreachable!("unexpected rule: {:?}", pair.as_rule()),
106 })
107}