1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use std::error::Error;

use pest::Parser;
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "filter/grammar.pest"]
pub struct Filter {}

#[derive(Clone, Debug, PartialEq)]
pub struct Field {
    pub name: String,
    pub labels: Vec<String>,
}

pub fn parse_filter(input: &str) -> Result<Vec<Field>, Box<dyn Error>> {
    let mut fields = Vec::new();
    let pairs = Filter::parse(Rule::filter, input)?;
    for pair in pairs {
        let mut name = String::new();
        let mut labels = Vec::new();

        let inner_pairs = pair.into_inner();
        for inner in inner_pairs {
            match inner.as_rule() {
                Rule::name => {
                    // according to clippy, this is a more efficient way of doing
                    // `name = inner.as_str().to_owned()`
                    inner.as_str().clone_into(&mut name);
                }
                Rule::label => {
                    labels.push(inner.as_str().to_owned());
                }
                _ => {}
            }
        }
        if !name.is_empty() {
            fields.push(Field { name, labels });
        }
    }
    Ok(fields)
}

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

    #[test]
    fn name_filter() -> Result<(), Box<dyn Error>> {
        let input = ".a_name";
        let expected = vec![Field {
            name: String::from("a_name"),
            labels: Vec::new(),
        }];
        let fields = parse_filter(input)?;
        assert_eq!(expected, fields);
        Ok(())
    }

    #[test]
    fn label_filter() -> Result<(), Box<dyn Error>> {
        let input = ".a_name[label=\"a_label\"]";
        let expected = vec![Field {
            name: String::from("a_name"),
            labels: vec![String::from("a_label")],
        }];
        let fields = parse_filter(input)?;
        assert_eq!(expected, fields);
        Ok(())
    }

    #[test]
    fn traversal_filter() -> Result<(), Box<dyn Error>> {
        let input = ".a_name[label=\"a_label\"].another_name[label=\"another_label\"].third_name";
        let expected = vec![
            Field {
                name: String::from("a_name"),
                labels: vec![String::from("a_label")],
            },
            Field {
                name: String::from("another_name"),
                labels: vec![String::from("another_label")],
            },
            Field {
                name: String::from("third_name"),
                labels: Vec::new(),
            },
        ];
        let fields = parse_filter(input)?;
        assert_eq!(expected, fields);
        Ok(())
    }
}