json_walk 0.1.0

Access values in your JSON data using string paths (e.g. "result.item[0].value").
Documentation
use anyhow::anyhow;
use regex::Regex;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Segment {
    /// The give key is not a list.
    Key(String),
    /// The given key has an index, usually will get this if the key is a list type. Example: item[0]
    Index(String, usize),
}

/// Parsers a json path
#[derive(Debug, Clone)]
pub struct JsonPathParser {}

impl JsonPathParser {
    /// Parses the given JSON path.Returns a vector containing each segment of the path.
    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)
    }

    /// Determines the Segment variant
    /// Parameters:
    /// value - The index value. Example: item[0], first_name, last_name
    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())
    }
}