postgrest_query_parser/ast/
filter.rs1use std::iter::Peekable;
2
3use crate::ast::{Ast, Error};
4use crate::lexer::{Lexer, SpanType};
5
6pub 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}