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;
19const MAX_VAL: i64 = 9007199254740991; const MIN_VAL: i64 = -9007199254740991; pub(super) 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(validate_range(
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}
240fn validate_range(val: i64) -> Result<i64, JsonPathError> {
241 if val > MAX_VAL || val < MIN_VAL {
242 Err(JsonPathError::InvalidJsonPath(format!(
243 "Value {} is out of range",
244 val
245 )))
246 } else {
247 Ok(val)
248 }
249}
250pub fn slice_selector(rule: Pair<Rule>) -> Parsed<(Option<i64>, Option<i64>, Option<i64>)> {
251 let mut start = None;
252 let mut end = None;
253 let mut step = None;
254 let get_int = |r: Pair<Rule>| r.as_str().trim().parse::<i64>().map_err(|e| (e, "int"));
255
256 for r in rule.into_inner() {
257 match r.as_rule() {
258 Rule::start => start = Some(validate_range(get_int(r)?)?),
259 Rule::end => end = Some(validate_range(get_int(r)?)?),
260 Rule::step => {
261 step = {
262 if let Some(int) = r.into_inner().next() {
263 Some(validate_range(get_int(int)?)?)
264 } else {
265 None
266 }
267 }
268 }
269
270 _ => return Err(r.into()),
271 }
272 }
273 Ok((start, end, step))
274}
275
276pub fn singular_query(rule: Pair<Rule>) -> Parsed<SingularQuery> {
277 let query = next_down(rule)?;
278 let segments = singular_query_segments(next_down(query.clone())?)?;
279 match query.as_rule() {
280 Rule::rel_singular_query => Ok(SingularQuery::Current(segments)),
281 Rule::abs_singular_query => Ok(SingularQuery::Root(segments)),
282 _ => Err(query.into()),
283 }
284}
285
286pub fn comp_expr(rule: Pair<Rule>) -> Parsed<Comparison> {
287 let mut children = rule.into_inner();
288
289 let lhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
290 let op = children
291 .next()
292 .ok_or(JsonPathError::empty("comparison"))?
293 .as_str();
294 let rhs = comparable(children.next().ok_or(JsonPathError::empty("comparison"))?)?;
295
296 Comparison::try_new(op, lhs, rhs)
297}
298
299fn validate_js_str(s: &str) -> Parsed<&str> {
303 for (i, c) in s.chars().enumerate() {
304 if c <= '\u{001F}' {
305 return Err(JsonPathError::InvalidJsonPath(format!(
306 "Invalid control character U+{:04X} at position {} in string literal",
307 c as u32, i
308 )));
309 }
310 }
311
312 Ok(s)
313}
314
315pub fn literal(rule: Pair<Rule>) -> Parsed<Literal> {
316 fn parse_number(num: &str) -> Parsed<Literal> {
317 let num = num.trim();
318
319 if num.contains('.') || num.contains('e') || num.contains('E') {
320 Ok(Literal::Float(num.parse::<f64>().map_err(|e| (e, num))?))
321 } else {
322 let num = num.trim().parse::<i64>().map_err(|e| (e, num))?;
323 if num > MAX_VAL || num < MIN_VAL {
324 Err(JsonPathError::InvalidNumber(format!(
325 "number out of bounds: {}",
326 num
327 )))
328 } else {
329 Ok(Literal::Int(num))
330 }
331 }
332 }
333
334 fn parse_string(string: &str) -> Parsed<Literal> {
335 let string = validate_js_str(string.trim())?;
336 if string.starts_with('\'') && string.ends_with('\'') {
337 Ok(Literal::String(string[1..string.len() - 1].to_string()))
338 } else if string.starts_with('"') && string.ends_with('"') {
339 Ok(Literal::String(string[1..string.len() - 1].to_string()))
340 } else {
341 Err(JsonPathError::InvalidJsonPath(format!(
342 "Invalid string literal `{}`",
343 string
344 )))
345 }
346 }
347
348 let first = next_down(rule)?;
349
350 match first.as_rule() {
351 Rule::string => parse_string(first.as_str()),
352 Rule::number => parse_number(first.as_str()),
353 Rule::bool => Ok(Literal::Bool(first.as_str().parse::<bool>()?)),
354 Rule::null => Ok(Literal::Null),
355
356 _ => Err(first.into()),
357 }
358}
359
360pub fn filter_atom(pair: Pair<Rule>) -> Parsed<FilterAtom> {
361 let rule = next_down(pair)?;
362
363 match rule.as_rule() {
364 Rule::paren_expr => {
365 let mut not = false;
366 let mut logic_expr = None;
367 for r in rule.into_inner() {
368 match r.as_rule() {
369 Rule::not_op => not = true,
370 Rule::logical_expr => logic_expr = Some(logical_expr(r)?),
371 _ => (),
372 }
373 }
374
375 logic_expr
376 .map(|expr| FilterAtom::filter(expr, not))
377 .ok_or("Logical expression is absent".into())
378 }
379 Rule::comp_expr => Ok(FilterAtom::cmp(Box::new(comp_expr(rule)?))),
380 Rule::test_expr => {
381 let mut not = false;
382 let mut test_expr = None;
383 for r in rule.into_inner() {
384 match r.as_rule() {
385 Rule::not_op => not = true,
386 Rule::test => test_expr = Some(test(r)?),
387 _ => (),
388 }
389 }
390
391 test_expr
392 .map(|expr| FilterAtom::test(expr, not))
393 .ok_or("Logical expression is absent".into())
394 }
395 _ => Err(rule.into()),
396 }
397}
398
399pub fn comparable(rule: Pair<Rule>) -> Parsed<Comparable> {
400 let rule = next_down(rule)?;
401 match rule.as_rule() {
402 Rule::literal => Ok(Comparable::Literal(literal(rule)?)),
403 Rule::singular_query => Ok(Comparable::SingularQuery(singular_query(rule)?)),
404 Rule::function_expr => {
405 let tf = function_expr(rule)?;
406 if tf.is_comparable() {
407 Ok(Comparable::Function(tf))
408 } else {
409 Err(JsonPathError::InvalidJsonPath(format!(
410 "Function {} is not comparable",
411 tf.to_string()
412 )))
413 }
414 }
415 _ => Err(rule.into()),
416 }
417}
418
419fn next_down(rule: Pair<Rule>) -> Parsed<Pair<Rule>> {
420 let rule_as_str = rule.as_str().to_string();
421 rule.into_inner()
422 .next()
423 .ok_or(JsonPathError::InvalidJsonPath(rule_as_str))
424}