Skip to main content

qdrant_edge/segment/json_path/
parse.rs

1use std::str::FromStr;
2
3use nom::branch::alt;
4use nom::bytes::complete::tag;
5use nom::character::complete::{char, digit1, none_of, satisfy};
6use nom::combinator::{all_consuming, map_res, recognize};
7use nom::multi::{many0, many1};
8use nom::sequence::{delimited, preceded};
9use nom::{IResult, Parser};
10
11use super::{JsonPath, JsonPathItem};
12
13impl FromStr for JsonPath {
14    type Err = ();
15
16    fn from_str(s: &str) -> Result<Self, Self::Err> {
17        match json_path(s) {
18            Ok(("", path)) => Ok(path),
19            _ => Err(()),
20        }
21    }
22}
23
24pub fn key_needs_quoting(s: &str) -> bool {
25    all_consuming(raw_str).parse(s).is_err()
26}
27
28fn json_path(input: &str) -> IResult<&str, JsonPath> {
29    let (input, first_key) = alt((raw_str.map(str::to_string), quoted_str)).parse(input)?;
30
31    let (input, rest) = many0(alt((
32        (preceded(char('.'), raw_str).map(|s| JsonPathItem::Key(s.to_string()))),
33        (preceded(char('.'), quoted_str).map(JsonPathItem::Key)),
34        (delimited(char('['), number, char(']')).map(JsonPathItem::Index)),
35        (tag("[]").map(|_| JsonPathItem::WildcardIndex)),
36    )))
37    .parse(input)?;
38
39    Ok((input, JsonPath { first_key, rest }))
40}
41
42fn raw_str(input: &str) -> IResult<&str, &str> {
43    recognize(many1(
44        satisfy(|c: char| c.is_alphanumeric() || c == '_' || c == '-').map(|_: char| ()),
45    ))
46    .parse(input)
47}
48
49fn quoted_str(input: &str) -> IResult<&str, String> {
50    let (input, _) = char('"')(input)?;
51    let (input, rest) = many0(none_of("\\\"")).parse(input)?;
52    let (input, _) = char('"')(input)?;
53    Ok((input, rest.iter().collect()))
54}
55
56fn number(input: &str) -> IResult<&str, usize> {
57    map_res(recognize(digit1), str::parse).parse(input)
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_parse() {
66        assert!("".parse::<JsonPath>().is_err());
67
68        assert_eq!(
69            "foo".parse(),
70            Ok(JsonPath {
71                first_key: "foo".to_string(),
72                rest: vec![],
73            })
74        );
75
76        assert_eq!(
77            "foo[1][50].bar-baz[].\"qux[.]quux\"".parse(),
78            Ok(JsonPath {
79                first_key: "foo".to_string(),
80                rest: vec![
81                    JsonPathItem::Index(1),
82                    JsonPathItem::Index(50),
83                    JsonPathItem::Key("bar-baz".to_string()),
84                    JsonPathItem::WildcardIndex,
85                    JsonPathItem::Key("qux[.]quux".to_string()),
86                ],
87            })
88        );
89    }
90
91    #[test]
92    fn test_key_needs_quoting() {
93        // Key needs no quoting
94        assert!(!key_needs_quoting("f"));
95        assert!(!key_needs_quoting("foo"));
96        assert!(!key_needs_quoting("foo_123-bar"));
97
98        // Key needs quoting
99        assert!(key_needs_quoting(""));
100        assert!(key_needs_quoting(" foo"));
101        assert!(key_needs_quoting("foo "));
102        assert!(key_needs_quoting("foo bar"));
103        assert!(key_needs_quoting("foo bar baz"));
104        assert!(key_needs_quoting("foo.bar.baz"));
105        assert!(key_needs_quoting("foo[]"));
106        assert!(key_needs_quoting("foo[0]"));
107    }
108}