jsonpath 0.1.1

JSONPath for Rust
Documentation

use pest::iterators::Pair;
use pest::Parser;

use errors::*;
use structs::Criterion;
use std::error::Error as StdError;

#[derive(Parser)]
#[grammar = "parser/grammar.pest"]
struct ExpressionParser;

pub fn parse(expression: &str) -> Result<Vec<Criterion>> {
    let pairs = ExpressionParser::parse(Rule::expression, expression)
        .map_err(|e| Error::from_kind(ErrorKind::Parse(e.description().to_owned())))?;

    for root in pairs.take(1) {
        let criteria = parse_tokens(root)?;
        return Ok(criteria);
    }
    unreachable!()
}

fn parse_tokens<'i>(element: Pair<'i, Rule>) -> Result<Vec<Criterion>> {
    let mut criteria: Vec<Criterion> = vec![];
    for token in element.into_inner() {
        match token.as_rule() {
            Rule::dollar => criteria.push(Criterion::Root),
            Rule::arobase => criteria.push(Criterion::Element),
            Rule::condition => {
                match token.into_inner().next().unwrap().as_rule() {
                    Rule::equal => {criteria.push(Criterion::Equal);},
                    Rule::different => {criteria.push(Criterion::Different);},
                    Rule::greater => {criteria.push(Criterion::Greater);},
                    Rule::lower => {criteria.push(Criterion::Lower);},
                    _ => {}
                }
            },
            Rule::literal => {
                let literal = token.into_inner().next().unwrap().as_str().to_owned();
                criteria.push(Criterion::Literal(literal))
            },
            Rule::number => {
                let value = token.as_str().parse::<i64>().unwrap();
                criteria.push(Criterion::Number(value))
            },
            Rule::float => {
                let value = token.as_str().parse::<f64>().unwrap();
                criteria.push(Criterion::Float(value))
            },
            Rule::filter => {
                let filter_criteria = parse_tokens(token)?;
                criteria.push(Criterion::Filter(filter_criteria))
            },
            Rule::child => {
                let ident = token.into_inner().next().unwrap().as_str().to_owned();
                criteria.push(Criterion::NamedChild(ident))
            },
            Rule::any_child => {
                criteria.push(Criterion::AnyChild)
            },
            Rule::indexed_child => {
                let index: usize = token.into_inner().next().unwrap().as_str().parse()?;
                criteria.push(Criterion::IndexedChild(index));
            }
            Rule::slice => {
                let mut iter = token.into_inner();
                let from: usize = iter.next().unwrap().as_str().parse()?;
                let to: usize = iter.next().unwrap().as_str().parse()?;
                criteria.push(Criterion::Slice(from..to));
            }
            Rule::slice_to => {
                let mut iter = token.into_inner();
                let to: usize = iter.next().unwrap().as_str().parse()?;
                criteria.push(Criterion::SliceTo(..to));
            }
            Rule::slice_from => {
                let mut iter = token.into_inner();
                let from: usize = iter.next().unwrap().as_str().parse()?;
                criteria.push(Criterion::SliceFrom(from));
            }
            lol => {
                println!("{:?}", lol);
                unreachable!()
            }
        }
    }

    Ok(criteria)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_root() {
        let exp = "$";
        let criteria = parse(exp).unwrap();
        assert_eq!(criteria, vec![Criterion::Root]);
    }

    #[test]
    fn test_child() {
        let exp = "$.book.title";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("book".to_owned()),
                Criterion::NamedChild("title".to_owned()),
            ]
        );
    }

    #[test]
    fn test_any_child() {
        let exp = "$.*.title";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::AnyChild,
                Criterion::NamedChild("title".to_owned()),
            ]
        );
    }

    #[test]
    fn test_indexed_child() {
        let exp = "$.books[34]";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("books".to_owned()),
                Criterion::IndexedChild(34),
            ]
        );
    }

    #[test]
    fn test_slice() {
        let exp = "$.books[4:7]";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("books".to_owned()),
                Criterion::Slice(4..7),
            ]
        );
    }

    #[test]
    fn test_slice_to() {
        let exp = "$.books[:4]";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("books".to_owned()),
                Criterion::SliceTo(..4),
            ]
        );
    }

    #[test]
    fn test_slice_from() {
        let exp = "$[2:]";
        let criteria = parse(exp).unwrap();
        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::SliceFrom(2),
            ]
        );
    }

    #[test]
    fn test_filter_absolute() {
        let exp = "$.books[?($.title == 'Sword Of Honor')]";
        let criteria = parse(exp).unwrap();

        let filter = vec![
            Criterion::Root,
            Criterion::NamedChild("title".to_owned()),
            Criterion::Equal,
            Criterion::Literal("Sword Of Honor".to_owned())
        ];

        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("books".to_owned()),
                Criterion::Filter(filter),
            ]
        );
    }

    #[test]
    fn test_filter_relative() {
        let exp = "$.books[?(@.title != 'Sword Of Honor')]";
        let criteria = parse(exp).unwrap();

        let filter = vec![
            Criterion::Element,
            Criterion::NamedChild("title".to_owned()),
            Criterion::Different,
            Criterion::Literal("Sword Of Honor".to_owned())
        ];

        assert_eq!(
            criteria,
            vec![
                Criterion::Root,
                Criterion::NamedChild("books".to_owned()),
                Criterion::Filter(filter),
            ]
        );
    }
}