hq_rs/filter/
parser.rs

1use pest::Parser;
2use pest_derive::Parser;
3
4use super::error::FilterError;
5
6#[derive(Parser)]
7#[grammar = "filter/grammar.pest"]
8pub struct Filter {}
9
10/// one segment of a filter ([`parse_filter`] returns `Vec<Field>`)
11///
12/// e.g. for the filter `'.foo{"bar"}.baz'` there are two segments:
13///
14/// * the name "foo" and the label "bar"
15/// * the name "baz"
16#[derive(Clone, Debug, PartialEq)]
17pub struct Field {
18    /// an attribute or block name
19    pub name: String,
20    /// block labels
21    pub labels: Vec<String>,
22}
23
24impl Field {
25    pub fn new(name: &str) -> Self {
26        Field {
27            name: name.to_string(),
28            labels: Vec::new(),
29        }
30    }
31
32    pub fn labeled(name: &str, labels: &[&str]) -> Self {
33        Field {
34            name: name.to_string(),
35            labels: labels.iter().map(|label| label.to_string()).collect(),
36        }
37    }
38}
39
40/// parse `input` and return a vector of [`Field`]s
41///
42/// a valid filter is one or more chained segments
43pub fn parse_filter(input: &str) -> Result<Vec<Field>, Box<FilterError<Rule>>> {
44    let mut fields = Vec::new();
45    let pairs = Filter::parse(Rule::filter, input)?;
46    for pair in pairs {
47        let mut name = String::new();
48        let mut labels = Vec::new();
49
50        let inner_pairs = pair.into_inner();
51        for inner in inner_pairs {
52            match inner.as_rule() {
53                Rule::name => {
54                    // according to clippy, this is a more efficient way of doing
55                    // `name = inner.as_str().to_owned()`
56                    inner.as_str().clone_into(&mut name);
57                }
58                Rule::quoted_name => {
59                    inner.as_str().clone_into(&mut name);
60                }
61                Rule::label => {
62                    labels.push(inner.as_str().to_owned());
63                }
64                _ => {}
65            }
66        }
67        if !name.is_empty() {
68            fields.push(Field { name, labels });
69        }
70    }
71    Ok(fields)
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn name_filter() {
80        let input = ".a_name";
81        let expected = vec![Field::new("a_name")];
82        let fields = parse_filter(input).expect("parse error");
83        assert_eq!(expected, fields);
84    }
85
86    #[test]
87    fn invalid_name_filter() {
88        let input = ".4foo";
89        let result = parse_filter(input);
90        assert!(result.is_err());
91    }
92
93    #[test]
94    fn label_filter() {
95        let input = ".a_name{\"a_label\"}";
96        let expected = vec![Field::labeled("a_name", &["a_label"])];
97        let fields = parse_filter(input).expect("parse error");
98        assert_eq!(expected, fields);
99    }
100
101    #[test]
102    fn traversal_filter() {
103        let input = ".a_name{\"a_label\"}.another_name{\"another_label\"}.third_name";
104        let expected = vec![
105            Field::labeled("a_name", &["a_label"]),
106            Field::labeled("another_name", &["another_label"]),
107            Field::new("third_name"),
108        ];
109        let fields = parse_filter(input).expect("parse error");
110        assert_eq!(expected, fields);
111    }
112}