1#![allow(clippy::empty_docs)]
2pub mod errors;
3mod macros;
4pub mod model;
5mod tests;
6
7use crate::parser::errors::JsonPathError;
8use crate::parser::model::{
9 Comparable, Comparison, Filter, FilterAtom, FnArg, JpQuery, Literal, Segment, Selector,
10 SingularQuery, SingularQuerySegment, Test, TestFunction,
11};
12
13use pest::iterators::Pair;
14use pest::Parser;
15
16#[derive(Parser)]
17#[grammar = "parser/grammar/json_path_9535.pest"]
18pub(super) struct JSPathParser;
19pub type Parsed<T> = Result<T, JsonPathError>;
23
24pub fn parse_json_path(jp_str: &str) -> Parsed<JpQuery> {
30 if jp_str != jp_str.trim() {
31 Err(JsonPathError::InvalidJsonPath(
32 "Leading or trailing whitespaces".to_string(),
33 ))
34 } else {
35 JSPathParser::parse(Rule::main, jp_str)
36 .map_err(Box::new)?
37 .next()
38 .ok_or(JsonPathError::UnexpectedPestOutput)
39 .and_then(next_down)
40 .and_then(jp_query)
41 }
42}
43
44pub fn jp_query(rule: Pair<Rule>) -> Parsed<JpQuery> {
45 Ok(JpQuery::new(segments(next_down(rule)?)?))
46}
47pub fn rel_query(rule: Pair<Rule>) -> Parsed<Vec<Segment>> {
48 segments(next_down(rule)?)
49}
50
51pub fn segments(rule: Pair<Rule>) -> Parsed<Vec<Segment>> {
52 let mut segments = vec![];
53 for r in rule.into_inner() {
54 segments.push(segment(next_down(r)?)?);
55 }
56 Ok(segments)
57}
58
59pub fn child_segment(rule: Pair<Rule>) -> Parsed<Segment> {
60 match rule.as_rule() {
61 Rule::wildcard_selector => Ok(Segment::Selector(Selector::Wildcard)),
62 Rule::member_name_shorthand => Ok(Segment::name(rule.as_str().trim())),
63 Rule::bracketed_selection => {
64 let mut selectors = vec![];
65 for r in rule.into_inner() {
66 selectors.push(selector(r)?);
67 }
68 if selectors.len() == 1 {
69 Ok(Segment::Selector(
70 selectors
71 .into_iter()
72 .next()
73 .ok_or(JsonPathError::empty("selector"))?,
74 ))
75 } else {
76 Ok(Segment::Selectors(selectors))
77 }
78 }
79 _ => Err(rule.into()),
80 }
81}
82
83pub fn segment(child: Pair<Rule>) -> Parsed<Segment> {
84 match child.as_rule() {
85 Rule::child_segment => {
86 let val = child.as_str().strip_prefix(".").unwrap_or_default();
87 if val != val.trim_start() {
88 Err(JsonPathError::InvalidJsonPath(format!(
89 "Invalid child segment `{}`",
90 child.as_str()
91 )))
92 } else {
93 child_segment(next_down(child)?)
94 }
95 }
96 Rule::descendant_segment => {
97 if child
98 .as_str()
99 .chars()
100 .nth(2)
101 .ok_or(JsonPathError::empty(child.as_str()))?
102 .is_whitespace()
103 {
104 Err(JsonPathError::InvalidJsonPath(format!(
105 "Invalid descendant segment `{}`",
106 child.as_str()
107 )))
108 } else {
109 Ok(Segment::Descendant(Box::new(child_segment(next_down(
110 child,
111 )?)?)))
112 }
113 }
114 _ => Err(child.into()),
115 }
116}
117
118pub fn selector(rule: Pair<Rule>) -> Parsed<Selector> {
119 let child = next_down(rule)?;
120 match child.as_rule() {
121 Rule::name_selector => Ok(Selector::Name(
122 validate_js_str(child.as_str().trim())?.to_string(),
123 )),
124 Rule::wildcard_selector => Ok(Selector::Wildcard),
125 Rule::index_selector => Ok(Selector::Index(
126 child
127 .as_str()
128 .trim()
129 .parse::<i64>()
130 .map_err(|e| (e, "wrong integer"))?,
131 )),
132 Rule::slice_selector => {
133 let (start, end, step) = slice_selector(child)?;
134 Ok(Selector::Slice(start, end, step))
135 }
136 Rule::filter_selector => Ok(Selector::Filter(logical_expr(next_down(child)?)?)),
137 _ => Err(child.into()),
138 }
139}
140
141pub fn function_expr(rule: Pair<Rule>) -> Parsed<TestFunction> {
142 let fn_str = rule.as_str();
143 let mut elems = rule.into_inner();
144 let name = elems
145 .next()
146 .map(|e| e.as_str())
147 .ok_or(JsonPathError::empty("function expression"))?;
148
149 if fn_str
151 .chars()
152 .nth(name.len())
153 .map(|c| c != '(')
154 .unwrap_or_default()
155 {
156 Err(JsonPathError::InvalidJsonPath(format!(
157 "Invalid function expression `{}`",
158 fn_str
159 )))
160 } else {
161 let mut args = vec![];
162 for arg in elems {
163 let next = next_down(arg)?;
164 match next.as_rule() {
165 Rule::literal => args.push(FnArg::Literal(literal(next)?)),
166 Rule::test => args.push(FnArg::Test(Box::new(test(next)?))),
167 Rule::logical_expr => args.push(FnArg::Filter(logical_expr(next)?)),
168
169 _ => return Err(next.into()),
170 }
171 }
172
173 TestFunction::try_new(name, args)
174 }
175}
176
177pub fn test(rule: Pair<Rule>) -> Parsed<Test> {
178 let child = next_down(rule)?;
179 match child.as_rule() {
180 Rule::jp_query => Ok(Test::AbsQuery(jp_query(child)?)),
181 Rule::rel_query => Ok(Test::RelQuery(rel_query(child)?)),
182 Rule::function_expr => Ok(Test::Function(Box::new(function_expr(child)?))),
183 _ => Err(child.into()),
184 }
185}
186
187pub fn logical_expr(rule: Pair<Rule>) -> Parsed<Filter> {
188 let mut ors = vec![];
189 for r in rule.into_inner() {
190 ors.push(logical_expr_and(r)?);
191 }
192 if ors.len() == 1 {
193 Ok(ors
194 .into_iter()
195 .next()
196 .ok_or(JsonPathError::empty("logical expression"))?)
197 } else {
198 Ok(Filter::Or(ors))
199 }
200}
201
202pub fn logical_expr_and(rule: Pair<Rule>) -> Parsed<Filter> {
203 let mut ands = vec![];
204 for r in rule.into_inner() {
205 ands.push(Filter::Atom(filter_atom(r)?));
206 }
207 if ands.len() == 1 {
208 Ok(ands
209 .into_iter()
210 .next()
211 .ok_or(JsonPathError::empty("logical expression"))?)
212 } else {
213 Ok(Filter::And(ands))
214 }
215}
216
217pub fn singular_query_segments(rule: Pair<Rule>) -> Parsed<Vec<SingularQuerySegment>> {
218 let mut segments = vec![];
219 for r in rule.into_inner() {
220 match r.as_rule() {
221 Rule::name_segment => {
222 segments.push(SingularQuerySegment::Name(
223 next_down(r)?.as_str().trim().to_string(),
224 ));
225 }
226 Rule::index_segment => {
227 segments.push(SingularQuerySegment::Index(
228 next_down(r)?
229 .as_str()
230 .trim()
231 .parse::<i64>()
232 .map_err(|e| (e, "int"))?,
233 ));
234 }
235 _ => return Err(r.into()),
236 }
237 }
238 Ok(segments)
239}
240
241pub fn slice_selector(rule: Pair<Rule>) -> Parsed<(Option<i64>, Option<i64>, Option<i64>)> {
242 let mut start = None;
243 let mut end = None;
244 let mut step = None;
245 let get_int = |r: Pair<Rule>| r.as_str().trim().parse::<i64>().map_err(|e| (e, "int"));
246
247 for r in rule.into_inner() {
248 match r.as_rule() {
249 Rule::start => start = Some(get_int(r)?),
250 Rule::end => end = Some(get_int(r)?),
251 Rule::step => {
252 step = {
253 if let Some(int) = r.into_inner().next() {
254 Some(get_int(int)?)
255 } else {
256 None
257 }
258 }
259 }
260
261 _ => return Err(r.into()),
262 }
263 }
264 Ok((start, end, step))
265}
266
267pub fn singular_query(rule: Pair<Rule>) -> Parsed<SingularQuery> {
268 let query = next_down(rule)?;
269 let segments = singular_query_segments(next_down(query.clone())?)?;
270 match query.as_rule() {
271 Rule::rel_singular_query => Ok(SingularQuery::Current(segments)),
272 Rule::abs_singular_query => Ok(SingularQuery::Root(segments)),
273 _ => Err(query.into()),
274 }
275}
276
277pub fn comp_expr(rule: Pair<Rule>) -> Parsed<Comparison> {
278 let mut children = rule.into_inner();
279
280 let lhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
281 let op = children
282 .next()
283 .ok_or(JsonPathError::empty("comparison"))?
284 .as_str();
285 let rhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
286
287 Comparison::try_new(op, lhs, rhs)
288}
289
290fn validate_js_str(s: &str) -> Parsed<&str> {
294 for (i, c) in s.chars().enumerate() {
295 if c <= '\u{001F}' {
296 return Err(JsonPathError::InvalidJsonPath(format!(
297 "Invalid control character U+{:04X} at position {} in string literal",
298 c as u32, i
299 )));
300 }
301 }
302
303 Ok(s)
304}
305
306pub fn literal(rule: Pair<Rule>) -> Parsed<Literal> {
307 fn parse_number(num: &str) -> Parsed<Literal> {
308 let num = num.trim();
309
310 if num.contains('.') || num.contains('e') || num.contains('E') {
311 Ok(Literal::Float(num.parse::<f64>().map_err(|e| (e, num))?))
312 } else {
313 Ok(Literal::Int(num.trim().parse::<i64>().map_err(|e| (e, num))?))
314 }
315 }
316
317 fn parse_string(string: &str) -> Parsed<Literal> {
318 let string = validate_js_str(string.trim())?;
319 if string.starts_with('\'') && string.ends_with('\'') {
320 Ok(Literal::String(string[1..string.len() - 1].to_string()))
321 } else if string.starts_with('"') && string.ends_with('"') {
322 Ok(Literal::String(string[1..string.len() - 1].to_string()))
323 } else {
324 Err(JsonPathError::InvalidJsonPath(format!(
325 "Invalid string literal `{}`",
326 string
327 )))
328 }
329 }
330
331 let first = next_down(rule)?;
332
333 match first.as_rule() {
334 Rule::string => parse_string(first.as_str()),
335 Rule::number => parse_number(first.as_str()),
336 Rule::bool => Ok(Literal::Bool(first.as_str().parse::<bool>()?)),
337 Rule::null => Ok(Literal::Null),
338
339 _ => Err(first.into()),
340 }
341}
342
343pub fn filter_atom(pair: Pair<Rule>) -> Parsed<FilterAtom> {
344 let rule = next_down(pair)?;
345
346 match rule.as_rule() {
347 Rule::paren_expr => {
348 let mut not = false;
349 let mut logic_expr = None;
350 for r in rule.into_inner() {
351 match r.as_rule() {
352 Rule::not_op => not = true,
353 Rule::logical_expr => logic_expr = Some(logical_expr(r)?),
354 _ => (),
355 }
356 }
357
358 logic_expr
359 .map(|expr| FilterAtom::filter(expr, not))
360 .ok_or("Logical expression is absent".into())
361 }
362 Rule::comp_expr => Ok(FilterAtom::cmp(Box::new(comp_expr(rule)?))),
363 Rule::test_expr => {
364 let mut not = false;
365 let mut test_expr = None;
366 for r in rule.into_inner() {
367 match r.as_rule() {
368 Rule::not_op => not = true,
369 Rule::test => test_expr = Some(test(r)?),
370 _ => (),
371 }
372 }
373
374 test_expr
375 .map(|expr| FilterAtom::test(expr, not))
376 .ok_or("Logical expression is absent".into())
377 }
378 _ => Err(rule.into()),
379 }
380}
381
382pub fn comparable(rule: Pair<Rule>) -> Parsed<Comparable> {
383 let rule = next_down(rule)?;
384 match rule.as_rule() {
385 Rule::literal => Ok(Comparable::Literal(literal(rule)?)),
386 Rule::singular_query => Ok(Comparable::SingularQuery(singular_query(rule)?)),
387 Rule::function_expr => {
388 let tf = function_expr(rule)?;
389 if tf.is_comparable() {
390 Ok(Comparable::Function(tf))
391 } else {
392 Err(JsonPathError::InvalidJsonPath(format!(
393 "Function {} is not comparable",
394 tf.to_string()
395 )))
396 }
397 }
398 _ => Err(rule.into()),
399 }
400}
401
402fn next_down(rule: Pair<Rule>) -> Parsed<Pair<Rule>> {
403 let rule_as_str = rule.as_str().to_string();
404 rule.into_inner()
405 .next()
406 .ok_or(JsonPathError::InvalidJsonPath(rule_as_str))
407}