Skip to main content

lens/
lib.rs

1pub mod tokens;
2
3#[cfg(test)]
4mod tests {
5    use crate::tokens::{ error::TokenError, prelude::*, tokenizer::Tokenizer };
6
7    type TestResult<T = ()> = Result<T, TokenError>;
8
9    fn main_query(input: &str) -> Result<Token, TokenError> {
10        let tokens = Tokenizer::from_input(input)
11            .map_err(pretty)?
12            .collect::<Result<Vec<Token>, _>>()?;
13        let token = tokens.into_iter().last().unwrap();
14        Ok(token)
15    }
16
17    fn first_main_filter(input: &str) -> Result<Filter, TokenError> {
18        let tokens = Tokenizer::from_input(input)
19            .map_err(pretty)?
20            .collect::<Result<Vec<Token>, _>>()?;
21
22        let filter: Filter = tokens
23            .into_iter()
24            .find_map(|t| {
25                if let Token::Main(query) = t {
26                    query.into_iter().find_map(|qt| {
27                        match qt {
28                            QueryToken::Filter(filter) => { Some(filter) }
29                            QueryToken::Molecule(mol) =>
30                                mol.into_iter().find_map(|qt| {
31                                    if let QueryToken::Filter(filter) = qt {
32                                        Some(filter)
33                                    } else {
34                                        None
35                                    }
36                                }),
37                            _ => { None }
38                        }
39                    })
40                } else {
41                    None
42                }
43            })
44            .expect("No filter found in main query");
45
46        Ok(filter)
47    }
48
49    macro_rules! assert_main_query_eq {
50        ($input:expr, $expected:expr, debug) => {
51            let actual = main_query($input)?;
52            println!("Resolved Type: {:#?}", actual);
53            println!("Input: {}", $input);
54            println!("Resolved: {actual}");
55            assert_eq!(actual, $expected);
56        };
57        ($input:expr, $expected:expr) => {
58            let actual = main_query($input)?;
59            assert_eq!(actual, $expected);
60        };
61    }
62
63    macro_rules! assert_first_main_filter_eq {
64        ($input:expr, $expected:expr) => {
65            let actual = first_main_filter($input)?;
66            assert_eq!(actual, $expected);
67        };
68    }
69
70    #[inline]
71    pub(crate) fn pretty<D: std::fmt::Display>(err: D) -> D {
72        eprintln!("{err}");
73        err
74    }
75
76    /* #[test]
77    fn goal() -> TestResult {
78        // todo: lambda lens
79        let input =
80            r#"
81            'names: **.departments.**.staff.name;
82            'is_name('name): ~ { name == 'name };
83            'age('name): **.users.**.*~{ 'is_name('name) }.age;
84            'user('name): -> { name = 'name, age = 'age('name) };
85            'user('names)
86        "#.trim();
87
88        let tokens = Tokenizer::from_input(input)
89            .map_err(pretty)?
90            .collect::<Result<Vec<Token>, _>>()?;
91        Ok(())
92    } */
93
94    #[test]
95    fn string() -> TestResult {
96        let input = "\"a\"";
97        let expected = Token::Main(vec![QueryToken::Atom(Atom::String(input.to_string()))].into());
98        assert_main_query_eq!(input, expected);
99
100        let input = r#""a.b.c""#;
101        let expected = Token::Main(vec![QueryToken::Atom(Atom::String(input.to_string()))].into());
102        assert_main_query_eq!(input, expected);
103        Ok(())
104    }
105
106    #[test]
107    fn index() -> TestResult {
108        let input = "0";
109        let expected = Token::Main(vec![QueryToken::Atom(Atom::Index(0))].into());
110        assert_main_query_eq!(input, expected);
111
112        let input = "a.0";
113        let expected = Token::Main(
114            vec![
115                QueryToken::Molecule(
116                    vec![
117                        QueryToken::Atom(Atom::Ident("a".to_string())),
118                        QueryToken::Atom(Atom::Index(0))
119                    ].into()
120                )
121            ].into()
122        );
123        assert_main_query_eq!(input, expected);
124        Ok(())
125    }
126
127    #[test]
128    fn float_basic() -> TestResult {
129        let expected = Filter(
130            vec![
131                FilterToken::Operation(
132                    FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F64(3.14)))])
133                )
134            ]
135        );
136        let input = "*~{ '3.14_f64 }";
137        assert_first_main_filter_eq!(input, expected);
138        let input = "*~{ '3.14f64 }";
139        assert_first_main_filter_eq!(input, expected);
140
141        let expected = Filter(
142            vec![
143                FilterToken::Operation(
144                    FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F32(3.14)))])
145                )
146            ]
147        );
148        let input = "*~{ '3.14_f32 }";
149        assert_first_main_filter_eq!(input, expected);
150        let input = "*~{ '3.14f32 }";
151        assert_first_main_filter_eq!(input, expected);
152
153        let expected = Filter(
154            vec![
155                FilterToken::Operation(
156                    FilterOperation(vec![FilterOperationToken::Atom(Atom::Float(Float::F32(3.0)))])
157                )
158            ]
159        );
160        let input = "*~{ '3.0f32 }";
161        assert_first_main_filter_eq!(input, expected);
162        let input = "*~{ '3.0_f32 }";
163        assert_first_main_filter_eq!(input, expected);
164        Ok(())
165    }
166
167    #[test]
168    fn float_scientific() -> TestResult {
169        // in a query, floats shouldn't be parsed at all.
170        let input = "'3.14e2_f64";
171        let expected = Token::Main(
172            vec![
173                QueryToken::Molecule(
174                    Molecule(
175                        vec![
176                            QueryToken::Atom(
177                                Atom::Call(LensCall {
178                                    lens: "3".to_string().into(),
179                                    args: vec![],
180                                })
181                            ),
182                            QueryToken::Atom(Atom::Ident("14e2_f64".to_string()))
183                        ]
184                    )
185                )
186            ].into()
187        );
188        assert_main_query_eq!(input, expected);
189
190        // but in a filter they should be parsed as floats.
191        let input = "*~{ '3.14e2_f64 }";
192        let expected = Token::Main(
193            vec![
194                QueryToken::Molecule(
195                    vec![
196                        QueryToken::Atom(Atom::Wild(Wild::Once)),
197                        QueryToken::Filter(
198                            Filter(
199                                vec![
200                                    FilterToken::Operation(
201                                        FilterOperation(
202                                            vec![
203                                                FilterOperationToken::Atom(
204                                                    Atom::Float(Float::F64(3.14e2))
205                                                )
206                                            ]
207                                        )
208                                    )
209                                ]
210                            )
211                        )
212                    ].into()
213                )
214            ].into()
215        );
216        assert_main_query_eq!(input, expected);
217
218        // check other other sign
219        let input = "*~{ '3.14e+2f64 }";
220        let expected = Token::Main(
221            vec![
222                QueryToken::Molecule(
223                    vec![
224                        QueryToken::Atom(Atom::Wild(Wild::Once)),
225                        QueryToken::Filter(
226                            Filter(
227                                vec![
228                                    FilterToken::Operation(
229                                        FilterOperation(
230                                            vec![
231                                                FilterOperationToken::Atom(
232                                                    Atom::Float(Float::F64(3.14e2))
233                                                )
234                                            ]
235                                        )
236                                    )
237                                ]
238                            )
239                        )
240                    ].into()
241                )
242            ].into()
243        );
244        assert_main_query_eq!(input, expected);
245
246        Ok(())
247    }
248
249    #[test]
250    fn float_negated() -> TestResult {
251        let input = "a~{ -'2_f64 }";
252        let expected = Token::Main(
253            vec![
254                QueryToken::Molecule(
255                    vec![
256                        QueryToken::Atom(Atom::Ident("a".to_string())),
257                        QueryToken::Filter(
258                            Filter(
259                                vec![
260                                    FilterToken::Operation(
261                                        FilterOperation(
262                                            vec![
263                                                FilterOperationToken::Prefix(FilterPrefix::Neg),
264                                                FilterOperationToken::Atom(
265                                                    Atom::Float(Float::F64(2.0))
266                                                )
267                                            ]
268                                        )
269                                    )
270                                ]
271                            )
272                        )
273                    ].into()
274                )
275            ].into()
276        );
277
278        assert_main_query_eq!(input, expected);
279
280        Ok(())
281    }
282
283    #[test]
284    fn fail_query_postfix() -> TestResult {
285        let input = "a??";
286        let err = Tokenizer::from_input(input).expect_err("illegal double postfix");
287        assert!(matches!(err, TokenError::Parsing(_)));
288
289        let input = "a?!";
290        let err = Tokenizer::from_input(input).expect_err("illegal double postfix");
291        assert!(matches!(err, TokenError::Parsing(_)));
292
293        let input = "a!a";
294        let err = Tokenizer::from_input(input).expect_err("postfix between atoms, not a query");
295        assert!(matches!(err, TokenError::Parsing(_)));
296
297        let input = "a~{ b }?!";
298        let err = Tokenizer::from_input(input).expect_err("illegal triple postfix combination");
299        assert!(matches!(err, TokenError::Parsing(_)));
300
301        let input = "a~{ b }!?";
302        let err = Tokenizer::from_input(input).expect_err("illegal triple postfix combination");
303        assert!(matches!(err, TokenError::Parsing(_)));
304        Ok(())
305    }
306
307    #[test]
308    fn pass_query_postfix() -> TestResult {
309        let input = "a?.b!.c~{ d }!";
310        let expected = Token::Main(
311            vec![
312                QueryToken::Molecule(
313                    vec![
314                        QueryToken::Atom(Atom::Ident("a".to_string())),
315                        QueryToken::Postfix(Postfix::Optional),
316                        QueryToken::Atom(Atom::Ident("b".to_string())),
317                        QueryToken::Postfix(Postfix::Assertion),
318                        QueryToken::Atom(Atom::Ident("c".to_string())),
319                        QueryToken::Filter(
320                            vec![
321                                FilterToken::Operation(
322                                    FilterOperation(
323                                        vec![
324                                            FilterOperationToken::Atom(Atom::Ident("d".to_string()))
325                                        ]
326                                    )
327                                )
328                            ].into()
329                        ),
330                        QueryToken::Postfix(Postfix::Assertion)
331                    ].into()
332                )
333            ].into()
334        );
335
336        assert_main_query_eq!(input, expected);
337
338        Ok(())
339    }
340
341    #[test]
342    fn query_infix() -> TestResult {
343        let input = "0 | a | b & c | d & (e & f | g) `and` h `and` `foo` 1";
344        let expected = Token::Main(
345            vec![
346                QueryToken::Atom(Atom::Index(0)),
347                QueryToken::Infix(Infix::Union),
348                QueryToken::Atom(Atom::Ident("a".to_string())),
349                QueryToken::Infix(Infix::Union),
350
351                QueryToken::Atom(Atom::Ident("b".to_string())),
352                QueryToken::Infix(Infix::Intersection),
353                QueryToken::Atom(Atom::Ident("c".to_string())),
354
355                QueryToken::Infix(Infix::Union),
356
357                QueryToken::Atom(Atom::Ident("d".to_string())),
358                QueryToken::Infix(Infix::Intersection),
359                QueryToken::Group(
360                    vec![
361                        QueryToken::Atom(Atom::Ident("e".to_string())),
362                        QueryToken::Infix(Infix::Intersection),
363                        QueryToken::Atom(Atom::Ident("f".to_string())),
364                        QueryToken::Infix(Infix::Union),
365                        QueryToken::Atom(Atom::Ident("g".to_string()))
366                    ].into()
367                ),
368                QueryToken::Infix(Infix::Lens("and".to_string().into())),
369                QueryToken::Atom(Atom::Ident("h".to_string())),
370                QueryToken::Infix(Infix::Lens("and".to_string().into())),
371                QueryToken::Infix(Infix::Lens("foo".to_string().into())),
372                QueryToken::Atom(Atom::Index(1))
373            ].into()
374        );
375
376        assert_main_query_eq!(input, expected);
377        Ok(())
378    }
379
380    #[test]
381    fn query_def() -> TestResult {
382        let input = "'foo('a, 'b): 'b?.'a(1) | 'b~{ 'c }; 'foo(a, b)";
383        let expected = Token::Definition(Definition {
384            lens: LensIdent("foo".into()),
385            params: vec![LensParam(LensIdent("a".into())), LensParam(LensIdent("b".into()))],
386            body: LensBody::Query(
387                vec![
388                    QueryToken::Molecule(
389                        vec![
390                            QueryToken::Atom(
391                                Atom::Call(LensCall {
392                                    lens: LensIdent("b".to_string()),
393                                    args: vec![],
394                                })
395                            ),
396                            QueryToken::Postfix(Postfix::Optional),
397                            QueryToken::Atom(
398                                Atom::Call(LensCall {
399                                    lens: LensIdent("a".to_string()),
400                                    args: vec![Token::QueryToken(QueryToken::Atom(Atom::Index(1)))],
401                                })
402                            )
403                        ].into()
404                    ),
405                    QueryToken::Infix(Infix::Union),
406                    QueryToken::Molecule(
407                        vec![
408                            QueryToken::Atom(
409                                Atom::Call(LensCall {
410                                    lens: LensIdent("b".to_string()),
411                                    args: vec![],
412                                })
413                            ),
414                            QueryToken::Filter(
415                                Filter(
416                                    vec![
417                                        FilterToken::Operation(
418                                            FilterOperation(
419                                                vec![
420                                                    FilterOperationToken::Implicit(
421                                                        ImplicitFilterToken::Call(LensCall {
422                                                            lens: LensIdent("c".into()),
423                                                            args: vec![],
424                                                        })
425                                                    )
426                                                ]
427                                            )
428                                        )
429                                    ]
430                                )
431                            )
432                        ].into()
433                    )
434                ].into()
435            ),
436        });
437
438        let tok = Tokenizer::from_input(input).map_err(pretty)?;
439        let tokens = tok.collect::<Result<Vec<Token>, _>>()?;
440
441        let def = tokens.into_iter().next().unwrap();
442        assert_eq!(def, expected);
443
444        Ok(())
445    }
446}