postgrest_query_parser/ast/
filter.rs

1use std::iter::Peekable;
2
3use crate::ast::{Ast, Error};
4use crate::lexer::{Lexer, SpanType};
5
6// wauw this is a long line with a lot of text ect. This mean that this will break to the next line hopefully because of rustfmt config
7
8pub mod consts {
9    pub const EQ: &str = "eq";
10    pub const GT: &str = "gt";
11    pub const GTE: &str = "gte";
12    pub const LT: &str = "lt";
13    pub const LTE: &str = "lte";
14    pub const NEQ: &str = "neq";
15    pub const LIKE: &str = "like";
16    pub const ILIKE: &str = "ilike";
17    pub const MATCH: &str = "match";
18    pub const IMATCH: &str = "imatch";
19    pub const IN: &str = "in";
20    pub const IS: &str = "is";
21    pub const ISDISTINCT: &str = "isdistinct";
22    pub const FTS: &str = "fts";
23    pub const PLFTS: &str = "plfts";
24    pub const PHFTS: &str = "phfts";
25    pub const WFTS: &str = "wfts";
26    pub const CS: &str = "cs";
27    pub const CD: &str = "cd";
28    pub const OV: &str = "ov";
29    pub const SL: &str = "sl";
30    pub const SR: &str = "sr";
31    pub const NXR: &str = "nxr";
32    pub const NXL: &str = "nxl";
33    pub const ADJ: &str = "adj";
34    pub const NOT: &str = "not";
35    pub const OR: &str = "or";
36    pub const AND: &str = "and";
37    pub const ALL: &str = "all";
38    pub const ANY: &str = "any";
39}
40
41pub type Value = String;
42
43#[derive(Debug, PartialEq, Clone)]
44pub enum Filter {
45    And(Vec<InnerFilter>),
46    Or(Vec<InnerFilter>),
47    One(InnerFilter),
48    Not(Box<Filter>),
49}
50
51#[derive(Debug, PartialEq, Clone)]
52pub struct InnerFilter {
53    pub path: Path,
54    pub operator: Operator,
55    pub value: Value,
56}
57
58#[derive(Debug, PartialEq, Clone)]
59pub enum Path {
60    Leaf(String),
61    Nested(String, Box<Path>),
62}
63
64impl Path {
65    fn append(&mut self, value: &str) {
66        match self {
67            Path::Leaf(field) => {
68                *self = Path::Nested(field.to_string(), Box::new(Path::Leaf(value.to_string())));
69            }
70            Path::Nested(_field, inner) => inner.append(value),
71        }
72    }
73}
74
75#[derive(Debug, PartialEq, Clone)]
76pub enum Operator {
77    Equal,
78    GreaterThan,
79    GreaterThanEqual,
80    LessThan,
81    LessThanEqual,
82    NotEqual,
83    Like,
84    ILike,
85    Match,
86    IMatch,
87    In,
88    Is,
89    IsDistinct,
90    FullTextSearch,
91    PLFT,
92    PHFT,
93    WebFullTextSearch,
94    Contains,
95    Contained,
96    Overlap,
97    StrictlyLeft,
98    StrictlyRight,
99    NotExtentToTheRight,
100    NotExtentToTheLeft,
101    Adjacent,
102    Not,
103    Or,
104    And,
105    All,
106    Any,
107}
108
109impl Ast {
110    pub(crate) fn parse_filter<T>(
111        field: &str,
112        input: &str,
113        tokens: &mut Peekable<Lexer<T>>,
114    ) -> Result<Filter, Error>
115    where
116        T: Iterator<Item = char>,
117    {
118        match field {
119            consts::OR => {
120                return Err(Error::OperatorNotImplemented {
121                    found: consts::OR.to_string(),
122                    range: 0..0,
123                })
124            }
125            consts::AND => {
126                return Err(Error::OperatorNotImplemented {
127                    found: consts::AND.to_string(),
128                    range: 0..0,
129                })
130            }
131            _ => {
132                let mut not = false;
133                let mut operator = None;
134                let mut value = None;
135                let mut path = Path::Leaf(field.to_string());
136                let mut path_closed = false;
137
138                while let Some(token) = tokens.next() {
139                    match token.span_type {
140                        SpanType::PathSeparator => {}
141                        SpanType::String if operator.is_some() => {
142                            match &input[token.range.clone()] {
143                                consts::NOT => {
144                                    return Err(Error::InvalidNotOrdering {
145                                        range: token.range.clone(),
146                                    })
147                                }
148                                path_value => {
149                                    value = Some(path_value.to_string());
150                                }
151                            }
152                        }
153                        SpanType::String if path_closed => match &input[token.range.clone()] {
154                            consts::EQ => {
155                                operator = Some(Operator::Equal);
156                            }
157                            consts::NEQ => {
158                                operator = Some(Operator::NotEqual);
159                            }
160                            consts::GT => {
161                                operator = Some(Operator::GreaterThan);
162                            }
163                            consts::GTE => {
164                                operator = Some(Operator::GreaterThanEqual);
165                            }
166                            consts::LT => {
167                                operator = Some(Operator::LessThan);
168                            }
169                            consts::LTE => {
170                                operator = Some(Operator::LessThanEqual);
171                            }
172                            consts::NOT => {
173                                not = true;
174                            }
175
176                            operator => {
177                                return Err(Error::OperatorNotImplemented {
178                                    found: operator.to_string(),
179                                    range: token.range.clone(),
180                                })
181                            }
182                        },
183                        SpanType::String if !path_closed => {
184                            path.append(&input[token.range.clone()]);
185                        }
186                        SpanType::Equal => path_closed = true,
187                        SpanType::And => break,
188                        _ => {
189                            return Err(Error::InvalidToken {
190                                expected: SpanType::PathSeparator,
191                                found: token.span_type,
192                                range: token.range,
193                            })
194                        }
195                    }
196                }
197
198                let filter = Filter::One(InnerFilter {
199                    path,
200                    operator: operator.unwrap(),
201                    value: value.unwrap(),
202                });
203
204                if not {
205                    return Ok(Filter::Not(Box::new(filter)));
206                } else {
207                    return Ok(filter);
208                }
209            }
210        };
211    }
212}
213
214#[test]
215fn simple_filter() {
216    let input = "age=gte.18";
217    let lexer = Lexer::new(input.chars());
218    let expected = Ast {
219        filter: vec![Filter::One(InnerFilter {
220            path: Path::Leaf("age".to_string()),
221            operator: Operator::GreaterThanEqual,
222            value: "18".to_string(),
223        })],
224        ..Default::default()
225    };
226    let out = Ast::from_lexer(input, lexer).unwrap();
227
228    assert_eq!(expected, out);
229}
230
231#[test]
232fn simple_not_filter() {
233    let input = "age=not.gte.18";
234    let lexer = Lexer::new(input.chars());
235    let expected = Ast {
236        filter: vec![Filter::Not(Box::new(Filter::One(InnerFilter {
237            path: Path::Leaf("age".to_string()),
238            operator: Operator::GreaterThanEqual,
239            value: "18".to_string(),
240        })))],
241        ..Default::default()
242    };
243    let out = Ast::from_lexer(input, lexer).unwrap();
244
245    assert_eq!(expected, out);
246}
247
248#[test]
249fn invalid_not_filter() {
250    let input = "age=gte.not.18";
251    let lexer = Lexer::new(input.chars());
252    let expected = Error::InvalidNotOrdering { range: 8..11 };
253    let out = Ast::from_lexer(input, lexer).unwrap_err();
254
255    assert_eq!(expected, out);
256}
257
258#[test]
259fn multiple_simple_filter() {
260    let input = "age=gte.18&age=lt.100";
261    let lexer = Lexer::new(input.chars());
262    let expected = Ast {
263        filter: vec![
264            Filter::One(InnerFilter {
265                path: Path::Leaf("age".to_string()),
266                operator: Operator::GreaterThanEqual,
267                value: "18".to_string(),
268            }),
269            Filter::One(InnerFilter {
270                path: Path::Leaf("age".to_string()),
271                operator: Operator::LessThan,
272                value: "100".to_string(),
273            }),
274        ],
275        ..Default::default()
276    };
277    let out = Ast::from_lexer(input, lexer).unwrap();
278
279    assert_eq!(expected, out);
280}
281
282#[test]
283fn nested_path_filter() {
284    let input = "organization.projects.tasks.name=eq.Test";
285    let lexer = Lexer::new(input.chars());
286    let expected = Ast {
287        filter: vec![Filter::One(InnerFilter {
288            path: Path::Nested(
289                "organization".to_string(),
290                Box::new(Path::Nested(
291                    "projects".to_string(),
292                    Box::new(Path::Nested(
293                        "tasks".to_string(),
294                        Box::new(Path::Leaf("name".to_string())),
295                    )),
296                )),
297            ),
298            operator: Operator::Equal,
299            value: "Test".to_string(),
300        })],
301        ..Default::default()
302    };
303    let out = Ast::from_lexer(input, lexer).unwrap();
304
305    assert_eq!(expected, out);
306}