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
24const MAX_NESTING_DEPTH: usize = 128;
32
33pub fn parse_json_path(jp_str: &str) -> Parsed<JpQuery> {
39 check_nesting_depth(jp_str)?;
40 JSPathParser::parse(Rule::main, jp_str)
41 .map_err(Box::new)?
42 .next()
43 .ok_or(JsonPathError::UnexpectedPestOutput)
44 .and_then(next_down)
45 .and_then(jp_query)
46}
47
48fn check_nesting_depth(jp_str: &str) -> Parsed<()> {
57 let mut depth: usize = 0;
58 let mut string_delim: Option<char> = None;
59 let mut escaped = false;
60
61 for c in jp_str.chars() {
62 if let Some(delim) = string_delim {
63 if escaped {
64 escaped = false;
65 } else if c == '\\' {
66 escaped = true;
67 } else if c == delim {
68 string_delim = None;
69 }
70 continue;
71 }
72
73 match c {
74 '\'' | '"' => string_delim = Some(c),
75 '(' | '[' => {
76 depth += 1;
77 if depth > MAX_NESTING_DEPTH {
78 return Err(JsonPathError::MaxNestingDepthExceeded(MAX_NESTING_DEPTH));
79 }
80 }
81 ')' | ']' => depth = depth.saturating_sub(1),
82 _ => {}
83 }
84 }
85
86 Ok(())
87}
88
89pub fn jp_query(rule: Pair<Rule>) -> Parsed<JpQuery> {
90 Ok(JpQuery::new(segments(next_down(rule)?)?))
91}
92pub fn rel_query(rule: Pair<Rule>) -> Parsed<Vec<Segment>> {
93 segments(next_down(rule)?)
94}
95
96pub fn segments(rule: Pair<Rule>) -> Parsed<Vec<Segment>> {
97 let mut segments = vec![];
98 for r in rule.into_inner() {
99 segments.push(segment(next_down(r)?)?);
100 }
101 Ok(segments)
102}
103
104pub fn child_segment(rule: Pair<Rule>) -> Parsed<Segment> {
105 match rule.as_rule() {
106 Rule::wildcard_selector => Ok(Segment::Selector(Selector::Wildcard)),
107 Rule::member_name_shorthand => Ok(Segment::name(rule.as_str().trim())),
108 Rule::bracketed_selection => {
109 let mut selectors = vec![];
110 for r in rule.into_inner() {
111 selectors.push(selector(r)?);
112 }
113 if selectors.len() == 1 {
114 Ok(Segment::Selector(
115 selectors
116 .into_iter()
117 .next()
118 .ok_or(JsonPathError::empty("selector"))?,
119 ))
120 } else {
121 Ok(Segment::Selectors(selectors))
122 }
123 }
124 _ => Err(rule.into()),
125 }
126}
127
128pub fn segment(child: Pair<Rule>) -> Parsed<Segment> {
129 match child.as_rule() {
130 Rule::child_segment => {
131 let val = child.as_str().strip_prefix(".").unwrap_or_default();
132 if val != val.trim_start() {
133 Err(JsonPathError::InvalidJsonPath(format!(
134 "Invalid child segment `{}`",
135 child.as_str()
136 )))
137 } else {
138 child_segment(next_down(child)?)
139 }
140 }
141 Rule::descendant_segment => {
142 if child
143 .as_str()
144 .chars()
145 .nth(2)
146 .ok_or(JsonPathError::empty(child.as_str()))?
147 .is_whitespace()
148 {
149 Err(JsonPathError::InvalidJsonPath(format!(
150 "Invalid descendant segment `{}`",
151 child.as_str()
152 )))
153 } else {
154 Ok(Segment::Descendant(Box::new(child_segment(next_down(
155 child,
156 )?)?)))
157 }
158 }
159 _ => Err(child.into()),
160 }
161}
162
163pub fn selector(rule: Pair<Rule>) -> Parsed<Selector> {
164 let child = next_down(rule)?;
165 match child.as_rule() {
166 Rule::name_selector => Ok(Selector::Name(unquote_and_unescape(validate_js_str(
167 child.as_str().trim(),
168 )?)?)),
169 Rule::wildcard_selector => Ok(Selector::Wildcard),
170 Rule::index_selector => Ok(Selector::Index(
171 child
172 .as_str()
173 .trim()
174 .parse::<i64>()
175 .map_err(|e| (e, "wrong integer"))?,
176 )),
177 Rule::slice_selector => {
178 let (start, end, step) = slice_selector(child)?;
179 Ok(Selector::Slice(start, end, step))
180 }
181 Rule::filter_selector => Ok(Selector::Filter(logical_expr(next_down(child)?)?)),
182 _ => Err(child.into()),
183 }
184}
185
186pub fn function_expr(rule: Pair<Rule>) -> Parsed<TestFunction> {
187 let fn_str = rule.as_str();
188 let mut elems = rule.into_inner();
189 let name = elems
190 .next()
191 .map(|e| e.as_str())
192 .ok_or(JsonPathError::empty("function expression"))?;
193
194 if fn_str
196 .chars()
197 .nth(name.len())
198 .map(|c| c != '(')
199 .unwrap_or_default()
200 {
201 Err(JsonPathError::InvalidJsonPath(format!(
202 "Invalid function expression `{}`",
203 fn_str
204 )))
205 } else {
206 let mut args = vec![];
207 for arg in elems {
208 let next = next_down(arg)?;
209 match next.as_rule() {
210 Rule::literal => args.push(FnArg::Literal(literal(next)?)),
211 Rule::test => args.push(FnArg::Test(Box::new(test(next)?))),
212 Rule::logical_expr => args.push(FnArg::Filter(logical_expr(next)?)),
213
214 _ => return Err(next.into()),
215 }
216 }
217
218 TestFunction::try_new(name, args)
219 }
220}
221
222pub fn test(rule: Pair<Rule>) -> Parsed<Test> {
223 let child = next_down(rule)?;
224 match child.as_rule() {
225 Rule::jp_query => Ok(Test::AbsQuery(jp_query(child)?)),
226 Rule::rel_query => Ok(Test::RelQuery(rel_query(child)?)),
227 Rule::function_expr => Ok(Test::Function(Box::new(function_expr(child)?))),
228 _ => Err(child.into()),
229 }
230}
231
232pub fn logical_expr(rule: Pair<Rule>) -> Parsed<Filter> {
233 let mut ors = vec![];
234 for r in rule.into_inner() {
235 ors.push(logical_expr_and(r)?);
236 }
237 if ors.len() == 1 {
238 Ok(ors
239 .into_iter()
240 .next()
241 .ok_or(JsonPathError::empty("logical expression"))?)
242 } else {
243 Ok(Filter::Or(ors))
244 }
245}
246
247pub fn logical_expr_and(rule: Pair<Rule>) -> Parsed<Filter> {
248 let mut ands = vec![];
249 for r in rule.into_inner() {
250 ands.push(Filter::Atom(filter_atom(r)?));
251 }
252 if ands.len() == 1 {
253 Ok(ands
254 .into_iter()
255 .next()
256 .ok_or(JsonPathError::empty("logical expression"))?)
257 } else {
258 Ok(Filter::And(ands))
259 }
260}
261
262pub fn singular_query_segments(rule: Pair<Rule>) -> Parsed<Vec<SingularQuerySegment>> {
263 let mut segments = vec![];
264 for r in rule.into_inner() {
265 match r.as_rule() {
266 Rule::name_segment => {
267 let name = next_down(r)?;
268 segments.push(SingularQuerySegment::Name(match name.as_rule() {
269 Rule::name_selector => {
270 unquote_and_unescape(validate_js_str(name.as_str().trim())?)?
271 }
272 _ => name.as_str().trim().to_string(),
273 }));
274 }
275 Rule::index_segment => {
276 segments.push(SingularQuerySegment::Index(
277 next_down(r)?
278 .as_str()
279 .trim()
280 .parse::<i64>()
281 .map_err(|e| (e, "int"))?,
282 ));
283 }
284 _ => return Err(r.into()),
285 }
286 }
287 Ok(segments)
288}
289
290pub fn slice_selector(rule: Pair<Rule>) -> Parsed<(Option<i64>, Option<i64>, Option<i64>)> {
291 let mut start = None;
292 let mut end = None;
293 let mut step = None;
294 let get_int = |r: Pair<Rule>| r.as_str().trim().parse::<i64>().map_err(|e| (e, "int"));
295
296 for r in rule.into_inner() {
297 match r.as_rule() {
298 Rule::start => start = Some(get_int(r)?),
299 Rule::end => end = Some(get_int(r)?),
300 Rule::step => {
301 step = {
302 if let Some(int) = r.into_inner().next() {
303 Some(get_int(int)?)
304 } else {
305 None
306 }
307 }
308 }
309
310 _ => return Err(r.into()),
311 }
312 }
313 Ok((start, end, step))
314}
315
316pub fn singular_query(rule: Pair<Rule>) -> Parsed<SingularQuery> {
317 let query = next_down(rule)?;
318 let segments = singular_query_segments(next_down(query.clone())?)?;
319 match query.as_rule() {
320 Rule::rel_singular_query => Ok(SingularQuery::Current(segments)),
321 Rule::abs_singular_query => Ok(SingularQuery::Root(segments)),
322 _ => Err(query.into()),
323 }
324}
325
326pub fn comp_expr(rule: Pair<Rule>) -> Parsed<Comparison> {
327 let mut children = rule.into_inner();
328
329 let lhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
330 let op = children
331 .next()
332 .ok_or(JsonPathError::empty("comparison"))?
333 .as_str();
334 let rhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
335
336 Comparison::try_new(op, lhs, rhs)
337}
338
339fn validate_js_str(s: &str) -> Parsed<&str> {
343 for (i, c) in s.chars().enumerate() {
344 if c <= '\u{001F}' {
345 return Err(JsonPathError::InvalidJsonPath(format!(
346 "Invalid control character U+{:04X} at position {} in string literal",
347 c as u32, i
348 )));
349 }
350 }
351
352 Ok(s)
353}
354
355fn unquote_and_unescape(literal: &str) -> Parsed<String> {
358 let invalid =
359 || JsonPathError::InvalidJsonPath(format!("Invalid string literal `{}`", literal));
360
361 let body = literal
362 .strip_prefix('"')
363 .and_then(|s| s.strip_suffix('"'))
364 .or_else(|| {
365 literal
366 .strip_prefix('\'')
367 .and_then(|s| s.strip_suffix('\''))
368 })
369 .ok_or_else(invalid)?;
370
371 if !body.contains('\\') {
372 return Ok(body.to_string());
373 }
374
375 let mut unescaped = String::with_capacity(body.len());
376 let mut chars = body.chars();
377 while let Some(ch) = chars.next() {
378 if ch != '\\' {
379 unescaped.push(ch);
380 continue;
381 }
382 match chars.next().ok_or_else(invalid)? {
383 'b' => unescaped.push('\u{0008}'),
384 'f' => unescaped.push('\u{000C}'),
385 'n' => unescaped.push('\n'),
386 'r' => unescaped.push('\r'),
387 't' => unescaped.push('\t'),
388 '/' => unescaped.push('/'),
389 '\\' => unescaped.push('\\'),
390 '"' => unescaped.push('"'),
391 '\'' => unescaped.push('\''),
392 'u' => unescaped.push(unescape_unicode(&mut chars).ok_or_else(invalid)?),
393 _ => return Err(invalid()),
394 }
395 }
396 Ok(unescaped)
397}
398
399fn unescape_unicode(chars: &mut std::str::Chars) -> Option<char> {
401 fn hex4(chars: &mut std::str::Chars) -> Option<u32> {
402 let mut code = 0;
403 for _ in 0..4 {
404 code = code * 16 + chars.next()?.to_digit(16)?;
405 }
406 Some(code)
407 }
408
409 match hex4(chars)? {
410 high @ 0xD800..=0xDBFF => {
411 if chars.next()? != '\\' || chars.next()? != 'u' {
412 return None;
413 }
414 match hex4(chars)? {
415 low @ 0xDC00..=0xDFFF => {
416 char::from_u32(0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00))
417 }
418 _ => None,
419 }
420 }
421 0xDC00..=0xDFFF => None,
422 code => char::from_u32(code),
423 }
424}
425
426pub fn literal(rule: Pair<Rule>) -> Parsed<Literal> {
427 fn parse_number(num: &str) -> Parsed<Literal> {
428 let num = num.trim();
429
430 if num.contains('.') || num.contains('e') || num.contains('E') {
431 Ok(Literal::Float(num.parse::<f64>().map_err(|e| (e, num))?))
432 } else {
433 Ok(Literal::Int(
434 num.trim().parse::<i64>().map_err(|e| (e, num))?,
435 ))
436 }
437 }
438
439 fn parse_string(string: &str) -> Parsed<Literal> {
440 Ok(Literal::String(unquote_and_unescape(validate_js_str(
441 string.trim(),
442 )?)?))
443 }
444
445 let first = next_down(rule)?;
446
447 match first.as_rule() {
448 Rule::string => parse_string(first.as_str()),
449 Rule::number => parse_number(first.as_str()),
450 Rule::bool => Ok(Literal::Bool(first.as_str().parse::<bool>()?)),
451 Rule::null => Ok(Literal::Null),
452
453 _ => Err(first.into()),
454 }
455}
456
457pub fn filter_atom(pair: Pair<Rule>) -> Parsed<FilterAtom> {
458 let rule = next_down(pair)?;
459
460 match rule.as_rule() {
461 Rule::paren_expr => {
462 let mut not = false;
463 let mut logic_expr = None;
464 for r in rule.into_inner() {
465 match r.as_rule() {
466 Rule::not_op => not = true,
467 Rule::logical_expr => logic_expr = Some(logical_expr(r)?),
468 _ => (),
469 }
470 }
471
472 logic_expr
473 .map(|expr| FilterAtom::filter(expr, not))
474 .ok_or("Logical expression is absent".into())
475 }
476 Rule::comp_expr => Ok(FilterAtom::cmp(Box::new(comp_expr(rule)?))),
477 Rule::test_expr => {
478 let mut not = false;
479 let mut test_expr = None;
480 for r in rule.into_inner() {
481 match r.as_rule() {
482 Rule::not_op => not = true,
483 Rule::test => test_expr = Some(test(r)?),
484 _ => (),
485 }
486 }
487
488 test_expr
489 .map(|expr| FilterAtom::test(expr, not))
490 .ok_or("Logical expression is absent".into())
491 }
492 _ => Err(rule.into()),
493 }
494}
495
496pub fn comparable(rule: Pair<Rule>) -> Parsed<Comparable> {
497 let rule = next_down(rule)?;
498 match rule.as_rule() {
499 Rule::literal => Ok(Comparable::Literal(literal(rule)?)),
500 Rule::singular_query => Ok(Comparable::SingularQuery(singular_query(rule)?)),
501 Rule::function_expr => {
502 let tf = function_expr(rule)?;
503 if tf.is_comparable() {
504 Ok(Comparable::Function(tf))
505 } else {
506 Err(JsonPathError::InvalidJsonPath(format!(
507 "Function {} is not comparable",
508 tf.to_string()
509 )))
510 }
511 }
512 _ => Err(rule.into()),
513 }
514}
515
516fn next_down(rule: Pair<Rule>) -> Parsed<Pair<Rule>> {
517 let rule_as_str = rule.as_str().to_string();
518 rule.into_inner()
519 .next()
520 .ok_or(JsonPathError::InvalidJsonPath(rule_as_str))
521}