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