use anyhow::anyhow;
use regex::Regex;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Segment {
Key(String),
Index(String, usize),
}
#[derive(Debug, Clone)]
pub struct JsonPathParser {}
impl JsonPathParser {
pub fn parse(path: &str) -> anyhow::Result<Vec<Segment>> {
let mut data: Vec<Segment> = Vec::new();
for value in path.split(".") {
data.push(Self::check_segment(value)?);
}
Ok(data)
}
pub fn check_segment(value: &str) -> anyhow::Result<Segment> {
let pattern = r"(\w+)(\[(\d+)\])?";
let re = Regex::new(pattern)?;
match re.captures(value) {
Some(captures) => {
let key = if let Some(value) = captures.get(1) {
value.as_str().to_string()
} else {
return Err(anyhow!("INVALID_PATH_KEY"));
};
if let Some(index) = captures.get(3) {
let index = index.as_str().parse::<usize>()?;
Ok(Segment::Index(key, index))
} else {
Ok(Segment::Key(key))
}
}
None => Err(anyhow!("INVALID_PATH")),
}
}
}
#[test]
fn test_json_path_parser() {
let result = JsonPathParser::check_segment("item[0]");
assert!(result.is_ok(), "{:#?}", result.err());
assert_eq!(Segment::Index("item".to_string(), 0), result.unwrap());
let result = JsonPathParser::check_segment("meta1");
assert!(result.is_ok(), "{:#?}", result.err());
assert_eq!(Segment::Key("meta1".to_string()), result.unwrap());
let test_cases = vec![
(
"result.item[0].value",
vec![
Segment::Key("result".to_string()),
Segment::Index("item".to_string(), 0),
Segment::Key("value".to_string()),
],
),
(
"result.item[1].details.color",
vec![
Segment::Key("result".to_string()),
Segment::Index("item".to_string(), 1),
Segment::Key("details".to_string()),
Segment::Key("color".to_string()),
],
),
(
"result.meta.count",
vec![
Segment::Key("result".to_string()),
Segment::Key("meta".to_string()),
Segment::Key("count".to_string()),
],
),
("status", vec![Segment::Key("status".to_string())]),
(
"result.item[0].details.price",
vec![
Segment::Key("result".to_string()),
Segment::Index("item".to_string(), 0),
Segment::Key("details".to_string()),
Segment::Key("price".to_string()),
],
),
("timestamp", vec![Segment::Key("timestamp".to_string())]),
];
for test_case in test_cases {
let parser = JsonPathParser::parse(test_case.0);
assert!(parser.is_ok(), "{:#?}", parser.err());
assert_eq!(test_case.1, parser.unwrap())
}
}