search_query_parser/
lib.rs

1mod bnf_approach;
2mod regex_approach;
3
4use crate::regex_approach::layered_query::LayeredQueries;
5use crate::regex_approach::query::Query;
6use eyre::Result;
7use serde::Serialize;
8
9pub fn parse_query_to_condition(query: &str) -> Result<Condition> {
10    LayeredQueries::parse(Query::new(query.into()))?.to_condition()
11}
12
13#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
14pub enum Condition {
15    None,
16    Keyword(String),
17    PhraseKeyword(String),
18    Not(Box<Condition>),
19    Operator(Operator, Vec<Condition>),
20}
21
22#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
23pub enum Operator {
24    And,
25    Or,
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use crate::{Condition, Operator};
32
33    mod normal_query {
34        use super::*;
35
36        #[test]
37        fn test_keywords_concat_with_spaces() {
38            let actual = parse_query_to_condition("word1 word2").unwrap();
39            assert_eq!(
40                actual,
41                Condition::Operator(
42                    Operator::And,
43                    vec![
44                        Condition::Keyword("word1".into()),
45                        Condition::Keyword("word2".into())
46                    ]
47                )
48            )
49        }
50
51        #[test]
52        fn test_keywords_concat_with_and_or() {
53            let actual = parse_query_to_condition("word1 OR word2 AND word3").unwrap();
54            assert_eq!(
55                actual,
56                Condition::Operator(
57                    Operator::Or,
58                    vec![
59                        Condition::Keyword("word1".into()),
60                        Condition::Operator(
61                            Operator::And,
62                            vec![
63                                Condition::Keyword("word2".into()),
64                                Condition::Keyword("word3".into()),
65                            ]
66                        )
67                    ]
68                )
69            )
70        }
71
72        #[test]
73        fn test_brackets() {
74            let actual = parse_query_to_condition("word1 AND (word2 OR word3)").unwrap();
75            assert_eq!(
76                actual,
77                Condition::Operator(
78                    Operator::And,
79                    vec![
80                        Condition::Keyword("word1".into()),
81                        Condition::Operator(
82                            Operator::Or,
83                            vec![
84                                Condition::Keyword("word2".into()),
85                                Condition::Keyword("word3".into()),
86                            ]
87                        )
88                    ]
89                )
90            )
91        }
92
93        #[test]
94        fn test_double_quote() {
95            let actual = parse_query_to_condition("\"word1 AND (word2 OR word3)\" word4").unwrap();
96            assert_eq!(
97                actual,
98                Condition::Operator(
99                    Operator::And,
100                    vec![
101                        Condition::PhraseKeyword("word1 AND (word2 OR word3)".into()),
102                        Condition::Keyword("word4".into()),
103                    ]
104                )
105            )
106        }
107
108        #[test]
109        fn test_minus() {
110            let actual = parse_query_to_condition("-word1 -\"word2\" -(word3 OR word4)").unwrap();
111            assert_eq!(
112                actual,
113                Condition::Operator(
114                    Operator::And,
115                    vec![
116                        Condition::Not(Box::new(Condition::Keyword("word1".into()))),
117                        Condition::Not(Box::new(Condition::PhraseKeyword("word2".into()))),
118                        Condition::Not(Box::new(Condition::Operator(
119                            Operator::Or,
120                            vec![
121                                Condition::Keyword("word3".into()),
122                                Condition::Keyword("word4".into())
123                            ]
124                        ))),
125                    ]
126                )
127            )
128        }
129
130        #[test]
131        fn test_full_pattern() {
132            let actual = parse_query_to_condition(
133                "(word1 and -word2) or ((\"phrase word 1\" or -\"phrase word 2\") and -(\" a long phrase word \" or word3))",
134            )
135                .unwrap();
136            assert_eq!(
137                actual,
138                Condition::Operator(
139                    Operator::Or,
140                    vec![
141                        Condition::Operator(
142                            Operator::And,
143                            vec![
144                                Condition::Keyword("word1".into()),
145                                Condition::Not(Box::new(Condition::Keyword("word2".into()))),
146                            ]
147                        ),
148                        Condition::Operator(
149                            Operator::And,
150                            vec![
151                                Condition::Operator(
152                                    Operator::Or,
153                                    vec![
154                                        Condition::PhraseKeyword("phrase word 1".into()),
155                                        Condition::Not(Box::new(Condition::PhraseKeyword(
156                                            "phrase word 2".into()
157                                        )))
158                                    ]
159                                ),
160                                Condition::Not(Box::new(Condition::Operator(
161                                    Operator::Or,
162                                    vec![
163                                        Condition::PhraseKeyword(" a long phrase word ".into()),
164                                        Condition::Keyword("word3".into())
165                                    ]
166                                )))
167                            ]
168                        ),
169                    ]
170                )
171            )
172        }
173    }
174
175    mod invalid_query {
176        use super::*;
177
178        #[test]
179        fn test_empty_brackets() {
180            let actual = parse_query_to_condition("A AND () AND B").unwrap();
181            assert_eq!(
182                actual,
183                Condition::Operator(
184                    Operator::And,
185                    vec![
186                        Condition::Keyword("A".into()),
187                        Condition::Keyword("B".into()),
188                    ]
189                )
190            )
191        }
192
193        #[test]
194        fn test_reverse_brackets() {
195            let actual = parse_query_to_condition("A OR B) AND (C OR D").unwrap();
196            assert_eq!(
197                actual,
198                Condition::Operator(
199                    Operator::Or,
200                    vec![
201                        Condition::Keyword("A".into()),
202                        Condition::Operator(
203                            Operator::And,
204                            vec![
205                                Condition::Keyword("B".into()),
206                                Condition::Keyword("C".into()),
207                            ]
208                        ),
209                        Condition::Keyword("D".into()),
210                    ]
211                )
212            )
213        }
214
215        #[test]
216        fn test_missing_brackets() {
217            let actual = parse_query_to_condition("(A OR B) AND (C").unwrap();
218            assert_eq!(
219                actual,
220                Condition::Operator(
221                    Operator::And,
222                    vec![
223                        Condition::Operator(
224                            Operator::Or,
225                            vec![
226                                Condition::Keyword("A".into()),
227                                Condition::Keyword("B".into()),
228                            ]
229                        ),
230                        Condition::Keyword("C".into()),
231                    ]
232                )
233            )
234        }
235
236        #[test]
237        fn test_invalid_nest_brackets() {
238            let actual = parse_query_to_condition("(((A OR B)) AND C").unwrap();
239            assert_eq!(
240                actual,
241                Condition::Operator(
242                    Operator::And,
243                    vec![
244                        Condition::Operator(
245                            Operator::Or,
246                            vec![
247                                Condition::Keyword("A".into()),
248                                Condition::Keyword("B".into()),
249                            ]
250                        ),
251                        Condition::Keyword("C".into()),
252                    ]
253                )
254            )
255        }
256
257        #[test]
258        fn test_no_keyword_in_brackets() {
259            let actual = parse_query_to_condition("A AND (\"\" OR \"\") AND B").unwrap();
260            assert_eq!(
261                actual,
262                Condition::Operator(
263                    Operator::And,
264                    vec![
265                        Condition::Keyword("A".into()),
266                        Condition::Keyword("B".into()),
267                    ]
268                )
269            )
270        }
271
272        #[test]
273        fn test_empty_phrase_keywords() {
274            let actual = parse_query_to_condition("A AND \"\" AND B").unwrap();
275            assert_eq!(
276                actual,
277                Condition::Operator(
278                    Operator::And,
279                    vec![
280                        Condition::Keyword("A".into()),
281                        Condition::Keyword("B".into()),
282                    ]
283                )
284            )
285        }
286
287        #[test]
288        fn test_invalid_double_quote() {
289            let actual = parse_query_to_condition("\"A\" OR \"B OR C").unwrap();
290            assert_eq!(
291                actual,
292                Condition::Operator(
293                    Operator::Or,
294                    vec![
295                        Condition::PhraseKeyword("A".into()),
296                        Condition::Keyword("B".into()),
297                        Condition::Keyword("C".into()),
298                    ]
299                )
300            )
301        }
302
303        #[test]
304        fn test_invalid_and_or() {
305            let actual = parse_query_to_condition("A AND OR B").unwrap();
306            assert_eq!(
307                actual,
308                Condition::Operator(
309                    Operator::Or,
310                    vec![
311                        Condition::Keyword("A".into()),
312                        Condition::Keyword("B".into()),
313                    ]
314                )
315            )
316        }
317    }
318
319    mod effective_query {
320        use super::*;
321
322        #[test]
323        fn test_unnecessary_nest_brackets() {
324            let actual = parse_query_to_condition("(A OR (B OR C)) AND D").unwrap();
325            assert_eq!(
326                actual,
327                Condition::Operator(
328                    Operator::And,
329                    vec![
330                        Condition::Operator(
331                            Operator::Or,
332                            vec![
333                                Condition::Keyword("A".into()),
334                                Condition::Keyword("B".into()),
335                                Condition::Keyword("C".into()),
336                            ]
337                        ),
338                        Condition::Keyword("D".into()),
339                    ]
340                )
341            )
342        }
343
344        #[test]
345        fn test_concat_brackets_without_space() {
346            let actual = parse_query_to_condition("A(B OR C)D").unwrap();
347            assert_eq!(
348                actual,
349                Condition::Operator(
350                    Operator::And,
351                    vec![
352                        Condition::Keyword("A".into()),
353                        Condition::Operator(
354                            Operator::Or,
355                            vec![
356                                Condition::Keyword("B".into()),
357                                Condition::Keyword("C".into()),
358                            ]
359                        ),
360                        Condition::Keyword("D".into()),
361                    ]
362                )
363            )
364        }
365
366        #[test]
367        fn test_concat_phrase_keywords_without_space() {
368            let actual = parse_query_to_condition("A\"B\"C").unwrap();
369            assert_eq!(
370                actual,
371                Condition::Operator(
372                    Operator::And,
373                    vec![
374                        Condition::Keyword("A".into()),
375                        Condition::PhraseKeyword("B".into()),
376                        Condition::Keyword("C".into()),
377                    ]
378                )
379            )
380        }
381    }
382}