1use pest_consume::{match_nodes, Error, Parser};
2type PathResult<T> = std::result::Result<T, Error<Rule>>;
3type Node<'i> = pest_consume::Node<'i, Rule, ()>;
4
5#[derive(Parser)]
6#[grammar = "path.pest"]
7struct PathParser;
8
9#[pest_consume::parser]
10impl PathParser {
11 fn EOI(_input: Node) -> PathResult<()> {
12 Ok(())
13 }
14 fn pure_key(input: Node) -> PathResult<String> {
15 Ok(input.as_str().to_string())
16 }
17 fn field(input: Node) -> PathResult<String> {
18 Ok(input.as_str().to_string())
19 }
20 fn value(input: Node) -> PathResult<String> {
21 Ok(input.as_str().to_string())
22 }
23 fn condition(input: Node) -> PathResult<Condition> {
24 Ok(match_nodes!(input.into_children();
25 [field(f), value(v)] => Condition { field: f, value: v }
26 ))
27 }
28 fn key(input: Node) -> PathResult<YamlPath> {
29 Ok(match_nodes!(input.into_children();
30 [pure_key(key), condition(cs)..] => YamlPath::Key(key, cs.collect(), None)
31 ))
32 }
33
34 fn index_number(input: Node) -> PathResult<Vec<usize>> {
35 Ok(vec![input.as_str().parse().unwrap()]) }
37 fn index_all(input: Node) -> PathResult<()> {
38 Ok(())
39 }
40 fn part(input: Node) -> PathResult<YamlPath> {
41 Ok(match_nodes!(input.into_children();
42 [index_all(_), condition(cs)..] => YamlPath::AllIndexes(cs.collect(), None),
43 [index_number(indexes), condition(cs)..] => YamlPath::Indexes(indexes, cs.collect(), None),
44 [key(r)] => r,
45
46 ))
47 }
48 fn path(input: Node) -> PathResult<YamlPath> {
49 Ok(match_nodes!(input.into_children();
50 [key(s), part(vals)..] => {
51 let mut path = s;
52 for val in vals.into_iter() {
53 path.insert(val);
54 }
55 path
56 }
57 ))
58 }
59 fn root_key(_input: Node) -> PathResult<()> {
60 Ok(())
61 }
62 fn root(input: Node) -> PathResult<YamlPath> {
63 Ok(match_nodes!(input.into_children();
64 [root_key(_s), condition(cs)..] => {
65 YamlPath::Root(cs.collect())
66 }
67 ))
68 }
69 fn final_path(input: Node) -> PathResult<YamlPath> {
70 Ok(match_nodes!(input.into_children();
71 [path(p), EOI(_)] => p,
72 [root(p), EOI(_)] => p
73 ))
74 }
75}
76
77fn parse(input_str: &str) -> PathResult<YamlPath> {
78 let inputs = PathParser::parse(Rule::final_path, input_str)?;
80 let input = inputs.single()?;
82 PathParser::final_path(input)
84}
85
86#[derive(Debug, Clone, PartialEq)]
87pub struct Condition {
88 pub field: String,
89 pub value: String,
90}
91
92#[derive(Debug, Clone, PartialEq)]
93pub enum YamlPath {
94 Root(Vec<Condition>),
95 Key(String, Vec<Condition>, Option<Box<YamlPath>>),
96 AllIndexes(Vec<Condition>, Option<Box<YamlPath>>),
97 Indexes(Vec<usize>, Vec<Condition>, Option<Box<YamlPath>>),
98}
99
100impl std::str::FromStr for YamlPath {
101 type Err = pest_consume::Error<Rule>;
102
103 fn from_str(path_str: &str) -> PathResult<Self> {
104 parse(path_str)
105 }
106}
107
108impl YamlPath {
109 pub fn insert(&mut self, p: YamlPath) {
110 match self {
111 YamlPath::Root(_) => *self = p,
112 YamlPath::Key(_, _, Some(ref mut o)) => o.insert(p),
113 YamlPath::AllIndexes(_, Some(ref mut o)) => o.insert(p),
114 YamlPath::Indexes(_, _, Some(ref mut o)) => o.insert(p),
115 YamlPath::Key(_, _, ref mut o) => *o = Some(Box::new(p)),
116 YamlPath::AllIndexes(_, ref mut o) => *o = Some(Box::new(p)),
117 YamlPath::Indexes(_, _, ref mut o) => *o = Some(Box::new(p)),
118 }
119 }
120}
121#[cfg(test)]
122mod test {
123 use super::*;
124 #[test]
125 fn parse() {
126 assert_eq!("".parse(), Ok(YamlPath::Root(Vec::new())));
127 assert_eq!(
128 "item".parse(),
129 Ok(YamlPath::Key("item".to_string(), Vec::new(), None))
130 );
131 assert_eq!(
132 "item.item2".parse(),
133 Ok(YamlPath::Key(
134 "item".to_string(),
135 Vec::new(),
136 Some(Box::new(YamlPath::Key(
137 "item2".to_string(),
138 Vec::new(),
139 None
140 )))
141 ))
142 );
143 assert_eq!(
144 "item.item2.item3".parse(),
145 Ok(YamlPath::Key(
146 "item".to_string(),
147 Vec::new(),
148 Some(Box::new(YamlPath::Key(
149 "item2".to_string(),
150 Vec::new(),
151 Some(Box::new(YamlPath::Key(
152 "item3".to_string(),
153 Vec::new(),
154 None
155 )))
156 )))
157 ))
158 );
159 }
160
161 #[test]
162 fn parse_with_conditions() {
163 assert_eq!(
164 r#"item|field=test.item2"#.parse(),
165 Ok(YamlPath::Key(
166 "item".to_string(),
167 vec![Condition {
168 field: "field".to_string(),
169 value: "test".to_string()
170 }],
171 Some(Box::new(YamlPath::Key(
172 "item2".to_string(),
173 Vec::new(),
174 None
175 )))
176 ))
177 );
178 assert_eq!(
179 r#"item|field=test|other=something.item2|yeah=oh"#.parse(),
180 Ok(YamlPath::Key(
181 "item".to_string(),
182 vec![
183 Condition {
184 field: "field".to_string(),
185 value: "test".to_string()
186 },
187 Condition {
188 field: "other".to_string(),
189 value: "something".to_string()
190 }
191 ],
192 Some(Box::new(YamlPath::Key(
193 "item2".to_string(),
194 vec![Condition {
195 field: "yeah".to_string(),
196 value: "oh".to_string()
197 }],
198 None
199 )))
200 ))
201 );
202 assert_eq!(
203 r#"item[*]|field=test|other=something.item2|yeah=oh"#.parse(),
204 Ok(YamlPath::Key(
205 "item".to_string(),
206 Vec::new(),
207 Some(Box::new(YamlPath::AllIndexes(
208 vec![
209 Condition {
210 field: "field".to_string(),
211 value: "test".to_string()
212 },
213 Condition {
214 field: "other".to_string(),
215 value: "something".to_string()
216 }
217 ],
218 Some(Box::new(YamlPath::Key(
219 "item2".to_string(),
220 vec![Condition {
221 field: "yeah".to_string(),
222 value: "oh".to_string()
223 }],
224 None
225 )))
226 )))
227 ))
228 );
229 }
230
231 #[test]
232 fn insert() {
233 let first: YamlPath = "minimum_marks".parse().unwrap();
234 let mut new: YamlPath = "parts[*].gaps|type=gapfill[*]".parse().unwrap();
235 new.insert(first);
236 assert_eq!(
237 new,
238 "parts[*].gaps|type=gapfill[*].minimum_marks"
239 .parse()
240 .unwrap()
241 );
242
243 let first: YamlPath = "minimum_marks".parse().unwrap();
244 let mut new: YamlPath = "parts[*]|type=gapfill.gaps[*]".parse().unwrap();
245 new.insert(first);
246 assert_eq!(
247 new,
248 "parts[*]|type=gapfill.gaps[*].minimum_marks"
249 .parse()
250 .unwrap()
251 );
252 }
253}