Skip to main content

rib/text/
mod.rs

1use crate::expr::Expr;
2use crate::ArmPattern;
3
4mod writer;
5
6use crate::text::writer::WriterError;
7
8pub fn from_string(input: impl AsRef<str>) -> Result<Expr, String> {
9    let trimmed = input.as_ref().trim();
10
11    // This check is kept for backward compatibility to support rib programs that were wrapped in `${..}`
12    // Rib's grammar doesn't support wrapping the expressions in`${}` anymore, and therefore
13    // we unwrap before calling Expr::from_text
14    if trimmed.starts_with("${") && trimmed.ends_with("}") {
15        let trimmed_open = trimmed.strip_prefix("${").unwrap();
16        let trimmed_closing = trimmed_open.strip_suffix('}').unwrap();
17        Expr::from_text(trimmed_closing)
18    } else {
19        Expr::from_text(input.as_ref())
20    }
21}
22
23pub fn to_string(expr: &Expr) -> Result<String, WriterError> {
24    writer::write_expr(expr)
25}
26
27pub fn to_string_arm_pattern(arm_pattern: &ArmPattern) -> Result<String, WriterError> {
28    writer::write_arm_pattern(arm_pattern)
29}
30
31#[cfg(test)]
32mod interpolation_tests {
33    use test_r::test;
34
35    use crate::{text, Expr};
36
37    #[test]
38    fn test_expr_wrapped_in_interpolation() {
39        let input = r#"${foo}"#;
40        let result = text::from_string(input);
41        assert_eq!(result, Ok(Expr::identifier_global("foo", None)));
42
43        let input = r#"${{foo}}"#;
44        let result = text::from_string(input);
45        assert_eq!(result, Ok(Expr::flags(vec!["foo".to_string()])));
46
47        let input = r#"${{foo: "bar"}}"#;
48        let result = text::from_string(input);
49        assert_eq!(
50            result,
51            Ok(Expr::record(vec![(
52                "foo".to_string(),
53                Expr::literal("bar")
54            )]))
55        );
56    }
57}
58
59#[cfg(test)]
60mod record_tests {
61    use bigdecimal::BigDecimal;
62    use test_r::test;
63
64    use crate::expr::*;
65    use crate::text::{from_string, to_string, Expr};
66    use crate::MatchArm;
67
68    #[test]
69    fn test_round_trip_simple_record_single() {
70        let input_expr = Expr::record(vec![(
71            "field".to_string(),
72            Expr::identifier_global("request", None),
73        )]);
74        let expr_str = to_string(&input_expr).unwrap();
75        let expected_str = "{field: request}".to_string();
76        let output_expr = from_string(expr_str.as_str()).unwrap();
77        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
78    }
79
80    #[test]
81    fn test_round_trip_read_write_record_multiple() {
82        let input_expr = Expr::record(vec![
83            (
84                "field1".to_string(),
85                Expr::identifier_global("request", None),
86            ),
87            (
88                "field2".to_string(),
89                Expr::identifier_global("request", None),
90            ),
91        ]);
92        let expr_str = to_string(&input_expr).unwrap();
93        let expected_str = "{field1: request, field2: request}".to_string();
94        let output_expr = from_string(expr_str.as_str()).unwrap();
95        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
96    }
97
98    #[test]
99    fn test_round_trip_read_write_record_of_literal() {
100        let input_expr = Expr::record(vec![
101            ("field1".to_string(), Expr::literal("hello")),
102            ("field2".to_string(), Expr::literal("world")),
103        ]);
104        let expr_str = to_string(&input_expr).unwrap();
105        let expected_str = r#"{field1: "hello", field2: "world"}"#.to_string();
106        let output_expr = from_string(expr_str.as_str()).unwrap();
107        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
108    }
109
110    #[test]
111    fn test_round_trip_read_write_record_of_number() {
112        let input_expr = Expr::record(vec![
113            ("field1".to_string(), Expr::number(BigDecimal::from(1))),
114            ("field2".to_string(), Expr::number(BigDecimal::from(2))),
115        ]);
116        let expr_str = to_string(&input_expr).unwrap();
117        let expected_str = "{field1: 1, field2: 2}".to_string();
118        let output_expr = from_string(expr_str.as_str()).unwrap();
119        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
120    }
121
122    #[test]
123    fn test_round_trip_read_write_record_of_select_field() {
124        let input_expr = Expr::record(vec![
125            (
126                "field1".to_string(),
127                Expr::select_field(Expr::identifier_global("request", None), "foo", None),
128            ),
129            (
130                "field2".to_string(),
131                Expr::select_field(Expr::identifier_global("request", None), "bar", None),
132            ),
133        ]);
134        let expr_str = to_string(&input_expr).unwrap();
135        let expected_str = "{field1: request.foo, field2: request.bar}".to_string();
136        let output_expr = from_string(expr_str.as_str()).unwrap();
137        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
138    }
139
140    #[test]
141    fn test_round_trip_read_write_record_of_select_index() {
142        let input_expr = Expr::record(vec![
143            (
144                "field1".to_string(),
145                Expr::select_index(
146                    Expr::identifier_global("request", None),
147                    Expr::number(BigDecimal::from(1)),
148                ),
149            ),
150            (
151                "field2".to_string(),
152                Expr::select_index(
153                    Expr::identifier_global("request", None),
154                    Expr::number(BigDecimal::from(2)),
155                ),
156            ),
157        ]);
158        let expr_str = to_string(&input_expr).unwrap();
159        let expected_str = "{field1: request[1], field2: request[2]}".to_string();
160        let output_expr = from_string(expr_str.as_str()).unwrap();
161        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
162    }
163
164    #[test]
165    fn test_round_trip_read_write_record_of_sequence() {
166        let input_expr = Expr::record(vec![
167            (
168                "field1".to_string(),
169                Expr::sequence(
170                    vec![
171                        Expr::identifier_global("request", None),
172                        Expr::identifier_global("request", None),
173                    ],
174                    None,
175                ),
176            ),
177            (
178                "field2".to_string(),
179                Expr::sequence(
180                    vec![
181                        Expr::identifier_global("request", None),
182                        Expr::identifier_global("request", None),
183                    ],
184                    None,
185                ),
186            ),
187        ]);
188        let expr_str = to_string(&input_expr).unwrap();
189        let expected_str = "{field1: [request, request], field2: [request, request]}".to_string();
190        let output_expr = from_string(expr_str.as_str()).unwrap();
191        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
192    }
193
194    #[test]
195    fn test_round_trip_read_write_record_of_record() {
196        let input_expr = Expr::record(vec![
197            (
198                "a".to_string(),
199                Expr::record(vec![
200                    ("ab".to_string(), Expr::identifier_global("request", None)),
201                    ("ac".to_string(), Expr::identifier_global("request", None)),
202                ]),
203            ),
204            (
205                "b".to_string(),
206                Expr::sequence(
207                    vec![Expr::record(vec![
208                        ("bc".to_string(), Expr::identifier_global("request", None)),
209                        ("bd".to_string(), Expr::identifier_global("request", None)),
210                    ])],
211                    None,
212                ),
213            ),
214        ]);
215        let expr_str = to_string(&input_expr).unwrap();
216        let expected_record_str =
217            "{a: {ab: request, ac: request}, b: [{bc: request, bd: request}]}".to_string();
218        let output_expr = from_string(expr_str.as_str()).unwrap();
219        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
220    }
221
222    #[test]
223    fn test_round_trip_read_write_record_of_tuple() {
224        let input_expr = Expr::record(vec![
225            (
226                "a".to_string(),
227                Expr::tuple(vec![
228                    Expr::identifier_global("request", None),
229                    Expr::identifier_global("worker", None),
230                ]),
231            ),
232            (
233                "b".to_string(),
234                Expr::tuple(vec![
235                    Expr::identifier_global("request", None),
236                    Expr::identifier_global("worker", None),
237                ]),
238            ),
239        ]);
240        let expr_str = to_string(&input_expr).unwrap();
241        let expected_record_str = "{a: (request, worker), b: (request, worker)}".to_string();
242        let output_expr = from_string(expr_str.as_str()).unwrap();
243        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
244    }
245
246    #[test]
247    fn test_round_trip_read_write_record_of_flags() {
248        let input_expr = Expr::record(vec![
249            (
250                "a".to_string(),
251                Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]),
252            ),
253            (
254                "b".to_string(),
255                Expr::flags(vec!["flag3".to_string(), "flag4".to_string()]),
256            ),
257        ]);
258        let expr_str = to_string(&input_expr).unwrap();
259        let expected_record_str = "{a: {flag1, flag2}, b: {flag3, flag4}}".to_string();
260        let output_expr = from_string(expr_str.as_str()).unwrap();
261        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
262    }
263
264    #[test]
265    fn test_round_trip_read_write_record_of_boolean() {
266        let input_expr = Expr::record(vec![
267            ("a".to_string(), Expr::boolean(true)),
268            ("b".to_string(), Expr::boolean(false)),
269        ]);
270        let expr_str = to_string(&input_expr).unwrap();
271        let expected_record_str = "{a: true, b: false}".to_string();
272        let output_expr = from_string(expr_str.as_str()).unwrap();
273        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
274    }
275
276    #[test]
277    fn test_round_trip_read_write_record_of_concatenation() {
278        let input_expr = Expr::record(vec![
279            (
280                "a".to_string(),
281                Expr::concat(vec![
282                    Expr::literal("user-id-1-"),
283                    Expr::select_field(Expr::identifier_global("request", None), "user-id-1", None),
284                ]),
285            ),
286            (
287                "b".to_string(),
288                Expr::concat(vec![
289                    Expr::literal("user-id-2-"),
290                    Expr::select_field(Expr::identifier_global("request", None), "user-id-2", None),
291                ]),
292            ),
293        ]);
294        let expr_str = to_string(&input_expr).unwrap();
295        let expected_record_str =
296            r#"{a: "user-id-1-${request.user-id-1}", b: "user-id-2-${request.user-id-2}"}"#
297                .to_string();
298        let output_expr = from_string(expr_str.as_str()).unwrap();
299        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
300    }
301
302    #[test]
303    fn test_round_trip_read_write_record_of_math_op() {
304        let input_expr = Expr::record(vec![
305            (
306                "a".to_string(),
307                Expr::greater_than(
308                    Expr::number(BigDecimal::from(1)),
309                    Expr::number(BigDecimal::from(2)),
310                ),
311            ),
312            (
313                "b".to_string(),
314                Expr::less_than(
315                    Expr::number(BigDecimal::from(1)),
316                    Expr::number(BigDecimal::from(2)),
317                ),
318            ),
319        ]);
320        let expr_str = to_string(&input_expr).unwrap();
321        let expected_record_str = "{a: 1 > 2, b: 1 < 2}".to_string();
322        let output_expr = from_string(expr_str.as_str()).unwrap();
323        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
324    }
325
326    #[test]
327    fn test_round_trip_read_write_record_of_if_condition() {
328        let input_expr = Expr::record(vec![
329            (
330                "a".to_string(),
331                Expr::cond(
332                    Expr::equal_to(
333                        Expr::select_field(Expr::identifier_global("request", None), "foo", None),
334                        Expr::literal("bar"),
335                    ),
336                    Expr::literal("success"),
337                    Expr::literal("failed"),
338                ),
339            ),
340            (
341                "b".to_string(),
342                Expr::cond(
343                    Expr::equal_to(
344                        Expr::select_field(Expr::identifier_global("request", None), "foo", None),
345                        Expr::literal("bar"),
346                    ),
347                    Expr::literal("success"),
348                    Expr::literal("failed"),
349                ),
350            ),
351        ]);
352        let expr_str = to_string(&input_expr).unwrap();
353        let expected_record_str = r#"{a: if request.foo == "bar" then "success" else "failed", b: if request.foo == "bar" then "success" else "failed"}"#.to_string();
354        let output_expr = from_string(expr_str.as_str()).unwrap();
355        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
356    }
357
358    #[test]
359    fn test_round_trip_read_write_record_of_pattern_match() {
360        let input_expr = Expr::record(vec![
361            (
362                "a".to_string(),
363                Expr::pattern_match(
364                    Expr::identifier_global("request", None),
365                    vec![
366                        MatchArm::new(
367                            ArmPattern::constructor(
368                                "ok",
369                                vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
370                            ),
371                            Expr::literal("success"),
372                        ),
373                        MatchArm::new(
374                            ArmPattern::constructor(
375                                "err",
376                                vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
377                            ),
378                            Expr::literal("failure"),
379                        ),
380                    ],
381                ),
382            ),
383            (
384                "b".to_string(),
385                Expr::pattern_match(
386                    Expr::identifier_global("request", None),
387                    vec![
388                        MatchArm::new(
389                            ArmPattern::constructor(
390                                "ok",
391                                vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
392                            ), // Use Constructor for ok
393                            Expr::literal("success"),
394                        ),
395                        MatchArm::new(
396                            ArmPattern::constructor(
397                                "err",
398                                vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
399                            ),
400                            Expr::pattern_match(
401                                Expr::identifier_global("request", None),
402                                vec![
403                                    MatchArm::new(
404                                        ArmPattern::constructor(
405                                            "ok",
406                                            vec![ArmPattern::literal(Expr::identifier_global(
407                                                "foo", None,
408                                            ))],
409                                        ),
410                                        Expr::literal("success"),
411                                    ),
412                                    MatchArm::new(
413                                        ArmPattern::constructor(
414                                            "err",
415                                            vec![ArmPattern::literal(Expr::identifier_global(
416                                                "msg", None,
417                                            ))],
418                                        ),
419                                        Expr::literal("failure"),
420                                    ),
421                                ],
422                            ),
423                        ),
424                    ],
425                ),
426            ),
427        ]);
428
429        let expr_str = to_string(&input_expr).unwrap();
430        let expected_record_str = r#"{a: match request {  ok(foo) => "success", err(msg) => "failure" } , b: match request {  ok(foo) => "success", err(msg) => match request {  ok(foo) => "success", err(msg) => "failure" }  } }"#.to_string();
431        let output_expr = from_string(expr_str.as_str()).unwrap();
432        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
433    }
434
435    #[test]
436    fn test_round_trip_record_of_constructor() {
437        let input_expr = Expr::record(vec![
438            ("a".to_string(), Expr::ok(Expr::literal("foo"), None)),
439            ("b".to_string(), Expr::err(Expr::literal("msg"), None)),
440        ]);
441        let expr_str = to_string(&input_expr).unwrap();
442        let expected_record_str = r#"{a: ok("foo"), b: err("msg")}"#.to_string();
443        let output_expr = from_string(expr_str.as_str()).unwrap();
444        assert_eq!((expr_str, input_expr), (expected_record_str, output_expr));
445    }
446
447    #[test]
448    fn test_round_trip_record_literal_invalid() {
449        let expr_str = r#"
450                 {body: golem:component/api.{get-character}(), headers: { x-test: 'foobar' } }
451            "#;
452
453        let result = from_string(expr_str);
454
455        assert!(result.is_err());
456
457        let expr_str = r#"
458                {body: golem:component/api.{get-character}(), headers: { x-test: "foobar" } }
459            "#;
460
461        let result = from_string(expr_str);
462
463        assert!(result.is_ok());
464    }
465}
466
467#[cfg(test)]
468mod sequence_tests {
469    use bigdecimal::BigDecimal;
470    use test_r::test;
471
472    use crate::expr::Expr;
473    use crate::text::{from_string, to_string};
474    use crate::{ArmPattern, MatchArm};
475
476    #[test]
477    fn test_round_trip_read_write_sequence_empty() {
478        let input_expr = Expr::sequence(vec![], None);
479        let expr_str = to_string(&input_expr).unwrap();
480        let expected_str = "[]".to_string();
481        let output_expr = from_string(expr_str.as_str()).unwrap();
482        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
483    }
484
485    // A few non-round-trip text based tests
486    #[test]
487    fn test_sequence_of_records_singleton() {
488        let expr_string = "[{bc: request}]";
489        let output_expr = from_string(expr_string).unwrap();
490        let expected_expr = Expr::sequence(
491            vec![Expr::record(vec![(
492                "bc".to_string(),
493                Expr::identifier_global("request", None),
494            )])],
495            None,
496        );
497        assert_eq!(output_expr, expected_expr);
498    }
499
500    #[test]
501    fn test_round_trip_read_write_sequence_of_request() {
502        let input_expr = Expr::sequence(
503            vec![
504                Expr::identifier_global("request", None),
505                Expr::identifier_global("request", None),
506            ],
507            None,
508        );
509        let expr_str = to_string(&input_expr).unwrap();
510        let expected_str = "[request, request]".to_string();
511        let output_expr = from_string(expr_str.as_str()).unwrap();
512        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
513    }
514
515    #[test]
516    fn test_round_trip_read_write_sequence_of_literal() {
517        let input_expr = Expr::sequence(vec![Expr::literal("hello"), Expr::literal("world")], None);
518        let expr_str = to_string(&input_expr).unwrap();
519        let expected_str = r#"["hello", "world"]"#.to_string();
520        let output_expr = from_string(expr_str.as_str()).unwrap();
521        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
522    }
523
524    #[test]
525    fn test_round_trip_read_write_sequence_of_select_field() {
526        let input_expr = Expr::sequence(
527            vec![
528                Expr::select_field(Expr::identifier_global("request", None), "field", None),
529                Expr::select_field(Expr::identifier_global("request", None), "field", None),
530            ],
531            None,
532        );
533        let expr_str = to_string(&input_expr).unwrap();
534        let expected_str = "[request.field, request.field]".to_string();
535        let output_expr = from_string(expr_str.as_str()).unwrap();
536        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
537    }
538
539    #[test]
540    fn test_round_trip_read_write_sequence_of_select_index() {
541        let input_expr = Expr::sequence(
542            vec![
543                Expr::select_index(
544                    Expr::identifier_global("request", None),
545                    Expr::number(BigDecimal::from(1)),
546                ),
547                Expr::select_index(
548                    Expr::identifier_global("request", None),
549                    Expr::number(BigDecimal::from(2)),
550                ),
551            ],
552            None,
553        );
554        let expr_str = to_string(&input_expr).unwrap();
555        let expected_str = "[request[1], request[2]]".to_string();
556        let output_expr = from_string(expr_str.as_str()).unwrap();
557        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
558    }
559
560    #[test]
561    fn test_round_trip_read_write_sequence_of_sequence() {
562        let input_expr = Expr::sequence(
563            vec![
564                Expr::sequence(
565                    vec![
566                        Expr::identifier_global("request", None),
567                        Expr::identifier_global("request", None),
568                    ],
569                    None,
570                ),
571                Expr::sequence(
572                    vec![
573                        Expr::identifier_global("request", None),
574                        Expr::identifier_global("request", None),
575                    ],
576                    None,
577                ),
578            ],
579            None,
580        );
581        let expr_str = to_string(&input_expr).unwrap();
582        let expected_str = "[[request, request], [request, request]]".to_string();
583        let output_expr = from_string(expr_str.as_str()).unwrap();
584        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
585    }
586
587    #[test]
588    fn test_round_trip_read_write_sequence_of_tuple() {
589        let input_expr = Expr::sequence(
590            vec![
591                Expr::tuple(vec![
592                    Expr::identifier_global("request", None),
593                    Expr::identifier_global("request", None),
594                ]),
595                Expr::tuple(vec![
596                    Expr::identifier_global("request", None),
597                    Expr::identifier_global("request", None),
598                ]),
599            ],
600            None,
601        );
602        let expr_str = to_string(&input_expr).unwrap();
603        let expected_str = "[(request, request), (request, request)]".to_string();
604        let output_expr = from_string(expr_str.as_str()).unwrap();
605        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
606    }
607
608    #[test]
609    fn test_round_trip_read_write_sequence_of_record() {
610        let input_expr = Expr::sequence(
611            vec![
612                Expr::record(vec![(
613                    "field".to_string(),
614                    Expr::identifier_global("request", None),
615                )]),
616                Expr::record(vec![(
617                    "field".to_string(),
618                    Expr::identifier_global("request", None),
619                )]),
620            ],
621            None,
622        );
623        let expr_str = to_string(&input_expr).unwrap();
624        let expected_str = "[{field: request}, {field: request}]".to_string();
625        let output_expr = from_string(expr_str.as_str()).unwrap();
626        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
627    }
628
629    #[test]
630    fn test_round_trip_read_write_sequence_of_flags() {
631        let input_expr = Expr::sequence(
632            vec![
633                Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]),
634                Expr::flags(vec!["flag3".to_string(), "flag4".to_string()]),
635            ],
636            None,
637        );
638        let expr_str = to_string(&input_expr).unwrap();
639        let expected_str = "[{flag1, flag2}, {flag3, flag4}]".to_string();
640        let output_expr = from_string(expr_str.as_str()).unwrap();
641        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
642    }
643
644    #[test]
645    fn test_round_trip_read_write_sequence_of_concat() {
646        let input_expr = Expr::sequence(
647            vec![
648                Expr::concat(vec![
649                    Expr::literal("user-id-1-"),
650                    Expr::select_field(Expr::identifier_global("request", None), "user-id-1", None),
651                ]),
652                Expr::concat(vec![
653                    Expr::literal("user-id-2-"),
654                    Expr::select_field(Expr::identifier_global("request", None), "user-id-2", None),
655                ]),
656            ],
657            None,
658        );
659        let expr_str = to_string(&input_expr).unwrap();
660        let expected_str =
661            r#"["user-id-1-${request.user-id-1}", "user-id-2-${request.user-id-2}"]"#.to_string();
662        let output_expr = from_string(expr_str.as_str()).unwrap();
663        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
664    }
665
666    #[test]
667    fn test_round_trip_read_write_sequence_of_math_op() {
668        let input_expr = Expr::sequence(
669            vec![
670                Expr::greater_than(
671                    Expr::number(BigDecimal::from(1)),
672                    Expr::number(BigDecimal::from(2)),
673                ),
674                Expr::less_than(
675                    Expr::number(BigDecimal::from(1)),
676                    Expr::number(BigDecimal::from(2)),
677                ),
678            ],
679            None,
680        );
681        let expr_str = to_string(&input_expr).unwrap();
682        let expected_str = "[1 > 2, 1 < 2]".to_string();
683        let output_expr = from_string(expr_str.as_str()).unwrap();
684        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
685    }
686
687    #[test]
688    fn test_round_trip_read_write_sequence_of_if_condition() {
689        let input_expr = Expr::sequence(
690            vec![
691                Expr::cond(
692                    Expr::equal_to(
693                        Expr::select_field(Expr::identifier_global("request", None), "foo", None),
694                        Expr::literal("bar"),
695                    ),
696                    Expr::literal("success"),
697                    Expr::literal("failed"),
698                ),
699                Expr::cond(
700                    Expr::equal_to(
701                        Expr::select_field(Expr::identifier_global("request", None), "foo", None),
702                        Expr::literal("bar"),
703                    ),
704                    Expr::literal("success"),
705                    Expr::literal("failed"),
706                ),
707            ],
708            None,
709        );
710        let expr_str = to_string(&input_expr).unwrap();
711        let expected_str = r#"[if request.foo == "bar" then "success" else "failed", if request.foo == "bar" then "success" else "failed"]"#.to_string();
712        let output_expr = from_string(expr_str.as_str()).unwrap();
713        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
714    }
715
716    #[test]
717    fn test_round_trip_read_write_sequence_of_pattern_match() {
718        let input_expr = Expr::sequence(
719            vec![
720                Expr::pattern_match(
721                    Expr::identifier_global("request", None),
722                    vec![
723                        MatchArm::new(
724                            ArmPattern::Constructor(
725                                "ok".to_string(),
726                                vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
727                            ),
728                            Expr::literal("success"),
729                        ),
730                        MatchArm::new(
731                            ArmPattern::Constructor(
732                                "err".to_string(),
733                                vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
734                            ),
735                            Expr::literal("failure"),
736                        ),
737                    ],
738                ),
739                Expr::pattern_match(
740                    Expr::identifier_global("request", None),
741                    vec![
742                        MatchArm::new(
743                            ArmPattern::Constructor(
744                                "ok".to_string(),
745                                vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
746                            ),
747                            Expr::literal("success"),
748                        ),
749                        MatchArm::new(
750                            ArmPattern::Constructor(
751                                "err".to_string(),
752                                vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
753                            ),
754                            Expr::pattern_match(
755                                Expr::identifier_global("request", None),
756                                vec![
757                                    MatchArm::new(
758                                        ArmPattern::Constructor(
759                                            "ok".to_string(),
760                                            vec![ArmPattern::literal(Expr::identifier_global(
761                                                "foo", None,
762                                            ))],
763                                        ), // Use Constructor for ok
764                                        Expr::literal("success"),
765                                    ),
766                                    MatchArm::new(
767                                        ArmPattern::Constructor(
768                                            "err".to_string(),
769                                            vec![ArmPattern::literal(Expr::identifier_global(
770                                                "msg", None,
771                                            ))],
772                                        ),
773                                        Expr::literal("failure"),
774                                    ),
775                                ],
776                            ),
777                        ),
778                    ],
779                ),
780            ],
781            None,
782        );
783
784        let expr_str = to_string(&input_expr).unwrap();
785        let expected_str = r#"[match request {  ok(foo) => "success", err(msg) => "failure" } , match request {  ok(foo) => "success", err(msg) => match request {  ok(foo) => "success", err(msg) => "failure" }  } ]"#.to_string();
786        let output_expr = from_string(expr_str.as_str()).unwrap();
787        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
788    }
789
790    #[test]
791    fn test_round_trip_read_write_sequence_of_constructor() {
792        let input_expr = Expr::sequence(
793            vec![
794                Expr::ok(Expr::literal("foo"), None),
795                Expr::err(Expr::literal("msg"), None),
796            ],
797            None,
798        );
799        let expr_str = to_string(&input_expr).unwrap();
800        let expected_str = "[ok(\"foo\"), err(\"msg\")]".to_string();
801        let output_expr = from_string(expr_str.as_str()).unwrap();
802        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
803    }
804}
805
806#[cfg(test)]
807mod tuple_tests {
808    use bigdecimal::BigDecimal;
809    use test_r::test;
810
811    use crate::expr::Expr;
812    use crate::text::{from_string, to_string};
813
814    #[test]
815    fn test_round_trip_read_write_tuple_empty() {
816        let input_expr = Expr::tuple(vec![]);
817        let expr_str = to_string(&input_expr).unwrap();
818        let expected_str = "()".to_string();
819        let output_expr = from_string(expr_str.as_str()).unwrap();
820        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
821    }
822
823    #[test]
824    fn test_round_trip_read_write_tuple_of_request() {
825        let input_expr = Expr::tuple(vec![
826            Expr::identifier_global("request", None),
827            Expr::identifier_global("request", None),
828        ]);
829        let expr_str = to_string(&input_expr).unwrap();
830        let expected_str = "(request, request)".to_string();
831        let output_expr = from_string(expr_str.as_str()).unwrap();
832        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
833    }
834
835    #[test]
836    fn test_round_trip_read_write_tuple_of_literal() {
837        let input_expr = Expr::tuple(vec![Expr::literal("hello"), Expr::literal("world")]);
838        let expr_str = to_string(&input_expr).unwrap();
839        let expected_str = r#"("hello", "world")"#.to_string();
840        let output_expr = from_string(expr_str.as_str()).unwrap();
841        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
842    }
843
844    #[test]
845    fn test_round_trip_read_write_tuple_of_select_field() {
846        let input_expr = Expr::tuple(vec![
847            Expr::select_field(Expr::identifier_global("request", None), "field", None),
848            Expr::select_field(Expr::identifier_global("request", None), "field", None),
849        ]);
850        let _expr_str = to_string(&input_expr).unwrap();
851        let _expected_str = "(request.field, request.field)".to_string();
852    }
853
854    #[test]
855    fn test_round_trip_read_write_tuple_of_select_index() {
856        let input_expr = Expr::tuple(vec![
857            Expr::select_index(
858                Expr::identifier_global("request", None),
859                Expr::number(BigDecimal::from(1)),
860            ),
861            Expr::select_index(
862                Expr::identifier_global("request", None),
863                Expr::number(BigDecimal::from(2)),
864            ),
865        ]);
866        let expr_str = to_string(&input_expr).unwrap();
867        let expected_str = "(request[1], request[2])".to_string();
868        let output_expr = from_string(expr_str.as_str()).unwrap();
869        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
870    }
871
872    #[test]
873    fn test_round_trip_read_write_tuple_of_tuple() {
874        let input_expr = Expr::tuple(vec![
875            Expr::tuple(vec![
876                Expr::identifier_global("request", None),
877                Expr::identifier_global("request", None),
878            ]),
879            Expr::tuple(vec![
880                Expr::identifier_global("request", None),
881                Expr::identifier_global("request", None),
882            ]),
883        ]);
884        let expr_str = to_string(&input_expr).unwrap();
885        let expected_str = "((request, request), (request, request))".to_string();
886        let output_expr = from_string(expr_str.as_str()).unwrap();
887        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
888    }
889
890    #[test]
891    fn test_round_trip_read_write_tuple_of_sequence() {
892        let input_expr = Expr::tuple(vec![
893            Expr::sequence(
894                vec![
895                    Expr::identifier_global("request", None),
896                    Expr::identifier_global("request", None),
897                ],
898                None,
899            ),
900            Expr::sequence(
901                vec![
902                    Expr::identifier_global("request", None),
903                    Expr::identifier_global("request", None),
904                ],
905                None,
906            ),
907        ]);
908        let expr_str = to_string(&input_expr).unwrap();
909        let expected_str = "([request, request], [request, request])".to_string();
910        let output_expr = from_string(expr_str.as_str()).unwrap();
911        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
912    }
913
914    #[test]
915    fn test_round_trip_read_write_tuple_of_record() {
916        let input_expr = Expr::tuple(vec![
917            Expr::record(vec![(
918                "field".to_string(),
919                Expr::identifier_global("request", None),
920            )]),
921            Expr::record(vec![(
922                "field".to_string(),
923                Expr::identifier_global("request", None),
924            )]),
925        ]);
926        let expr_str = to_string(&input_expr).unwrap();
927        let expected_str = "({field: request}, {field: request})".to_string();
928        let output_expr = from_string(expr_str.as_str()).unwrap();
929        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
930    }
931
932    #[test]
933    fn test_round_trip_read_write_tuple_of_flags() {
934        let input_expr = Expr::tuple(vec![
935            Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]),
936            Expr::flags(vec!["flag3".to_string(), "flag4".to_string()]),
937        ]);
938        let expr_str = to_string(&input_expr).unwrap();
939        let expected_str = "({flag1, flag2}, {flag3, flag4})".to_string();
940        let output_expr = from_string(expr_str.as_str()).unwrap();
941        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
942    }
943
944    #[test]
945    fn test_round_trip_read_write_tuple_of_concat() {
946        let input_expr = Expr::tuple(vec![
947            Expr::concat(vec![
948                Expr::literal("user-id-1-"),
949                Expr::select_field(Expr::identifier_global("request", None), "user-id-1", None),
950            ]),
951            Expr::concat(vec![
952                Expr::literal("user-id-2-"),
953                Expr::select_field(Expr::identifier_global("request", None), "user-id-2", None),
954            ]),
955        ]);
956        let expr_str = to_string(&input_expr).unwrap();
957        let expected_str =
958            r#"("user-id-1-${request.user-id-1}", "user-id-2-${request.user-id-2}")"#.to_string();
959        let output_expr = from_string(expr_str.as_str()).unwrap();
960        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
961    }
962
963    #[test]
964    fn test_round_trip_read_write_tuple_of_math_op() {
965        let input_expr = Expr::tuple(vec![
966            Expr::greater_than(
967                Expr::number(BigDecimal::from(1)),
968                Expr::number(BigDecimal::from(2)),
969            ),
970            Expr::less_than(
971                Expr::number(BigDecimal::from(1)),
972                Expr::number(BigDecimal::from(2)),
973            ),
974        ]);
975        let expr_str = to_string(&input_expr).unwrap();
976        let expected_str = "(1 > 2, 1 < 2)".to_string();
977        let output_expr = from_string(expr_str.as_str()).unwrap();
978        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
979    }
980
981    #[test]
982    fn test_round_trip_read_write_tuple_of_constructor() {
983        let input_expr = Expr::tuple(vec![
984            Expr::ok(Expr::literal("foo"), None),
985            Expr::err(Expr::literal("msg"), None),
986        ]);
987        let expr_str = to_string(&input_expr).unwrap();
988        let expected_str = r#"(ok("foo"), err("msg"))"#.to_string();
989        let output_expr = from_string(expr_str.as_str()).unwrap();
990        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
991    }
992}
993
994#[cfg(test)]
995mod simple_values_test {
996    use bigdecimal::BigDecimal;
997    use std::str::FromStr;
998    use test_r::test;
999
1000    use crate::expr::Expr;
1001    use crate::text::{from_string, to_string};
1002
1003    #[test]
1004    fn test_round_trip_read_write_literal() {
1005        let input_expr = Expr::literal("hello");
1006        let expr_str = to_string(&input_expr).unwrap();
1007        let expected_str = "\"hello\"".to_string();
1008        let output_expr = from_string(expr_str.as_str()).unwrap();
1009        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1010    }
1011
1012    #[test]
1013    fn test_round_trip_read_write_request() {
1014        let input_expr = Expr::identifier_global("request", None);
1015        let expr_str = to_string(&input_expr).unwrap();
1016        let expected_str = "request".to_string();
1017        let output_expr = from_string(expr_str.as_str()).unwrap();
1018        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1019    }
1020
1021    #[test]
1022    fn test_round_trip_read_write_number_float() {
1023        let input_expr = Expr::number(BigDecimal::from_str("1.1").unwrap());
1024        let expr_str = to_string(&input_expr).unwrap();
1025        let output_expr = from_string(expr_str.as_str()).unwrap();
1026        assert_eq!(input_expr, output_expr);
1027    }
1028
1029    #[test]
1030    fn test_round_trip_read_write_number_u64() {
1031        let input_expr = Expr::number(BigDecimal::from(1));
1032        let expr_str = to_string(&input_expr).unwrap();
1033        let expected_str = "1".to_string();
1034        let output_expr = from_string(expr_str.as_str()).unwrap();
1035        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1036    }
1037
1038    #[test]
1039    fn test_round_trip_read_write_number_i64() {
1040        let input_expr = Expr::number(BigDecimal::from(-1));
1041        let expr_str = to_string(&input_expr).unwrap();
1042        let expected_str = "-1".to_string();
1043        let output_expr = from_string(expr_str.as_str()).unwrap();
1044        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1045    }
1046
1047    #[test]
1048    fn test_round_trip_read_write_worker() {
1049        let input_expr = Expr::identifier_global("worker", None);
1050        let expr_str = to_string(&input_expr).unwrap();
1051        let expected_str = "worker".to_string();
1052        let output_expr = from_string(expr_str.as_str()).unwrap();
1053        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1054    }
1055
1056    #[test]
1057    fn test_round_trip_read_write_variable() {
1058        let input_expr = Expr::identifier_global("variable", None);
1059        let expr_str = to_string(&input_expr).unwrap();
1060        let expected_str = "variable".to_string();
1061        let output_expr = from_string(expr_str.as_str()).unwrap();
1062        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1063    }
1064
1065    #[test]
1066    fn test_round_trip_read_write_boolean() {
1067        let input_expr = Expr::boolean(true);
1068        let expr_str = to_string(&input_expr).unwrap();
1069        let expected_str = "true".to_string();
1070        let output_expr = from_string(expr_str.as_str()).unwrap();
1071        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1072    }
1073}
1074
1075#[cfg(test)]
1076mod let_tests {
1077    use bigdecimal::BigDecimal;
1078    use test_r::test;
1079
1080    use crate::expr::Expr;
1081    use crate::parser::type_name::TypeName;
1082    use crate::text::{from_string, to_string};
1083    use crate::{InferredType, VariableId};
1084
1085    #[test]
1086    fn test_round_trip_read_write_let() {
1087        let input_expr = Expr::expr_block(vec![
1088            Expr::let_binding("x", Expr::literal("hello"), None),
1089            Expr::let_binding("y", Expr::literal("bar"), None),
1090        ]);
1091        let expr_str = to_string(&input_expr).unwrap();
1092        let expected_str = "let x = \"hello\";\nlet y = \"bar\"".to_string();
1093        let output_expr = from_string(expr_str.as_str()).unwrap();
1094        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1095    }
1096
1097    #[test]
1098    fn test_round_trip_read_write_let_with_type_binding_str() {
1099        let input_expr = Expr::expr_block(vec![
1100            Expr::let_binding_with_variable_id(
1101                VariableId::global("x".to_string()),
1102                Expr::literal("hello"),
1103                Some(TypeName::Str),
1104            ),
1105            Expr::let_binding_with_variable_id(
1106                VariableId::global("y".to_string()),
1107                Expr::literal("bar"),
1108                Some(TypeName::Str),
1109            ),
1110        ]);
1111        let expr_str = to_string(&input_expr).unwrap();
1112        let expected_str = "let x: string = \"hello\";\nlet y: string = \"bar\"".to_string();
1113        let output_expr = from_string(expr_str.as_str()).unwrap();
1114        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1115    }
1116
1117    #[test]
1118    fn test_round_trip_read_write_let_with_type_binding_u8() {
1119        let input_expr = Expr::expr_block(vec![
1120            Expr::let_binding_with_variable_id(
1121                VariableId::global("x".to_string()),
1122                Expr::number(BigDecimal::from(1)),
1123                Some(TypeName::U8),
1124            ),
1125            Expr::let_binding_with_variable_id(
1126                VariableId::global("y".to_string()),
1127                Expr::number(BigDecimal::from(2)),
1128                Some(TypeName::U8),
1129            ),
1130        ]);
1131        let expr_str = to_string(&input_expr).unwrap();
1132        let expected_str = "let x: u8 = 1;\nlet y: u8 = 2".to_string();
1133        let output_expr = from_string(expr_str.as_str()).unwrap();
1134        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1135    }
1136
1137    #[test]
1138    fn test_round_trip_read_write_let_with_type_binding_u16() {
1139        let input_expr = Expr::expr_block(vec![
1140            Expr::let_binding_with_variable_id(
1141                VariableId::global("x".to_string()),
1142                Expr::number(BigDecimal::from(1)),
1143                Some(TypeName::U16),
1144            ),
1145            Expr::let_binding_with_variable_id(
1146                VariableId::global("y".to_string()),
1147                Expr::number(BigDecimal::from(2)),
1148                Some(TypeName::U16),
1149            ),
1150        ]);
1151        let expr_str = to_string(&input_expr).unwrap();
1152        let expected_str = "let x: u16 = 1;\nlet y: u16 = 2".to_string();
1153        let output_expr = from_string(expr_str.as_str()).unwrap();
1154        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1155    }
1156
1157    #[test]
1158    fn test_round_trip_read_write_let_with_type_binding_u32() {
1159        let input_expr = Expr::expr_block(vec![
1160            Expr::let_binding_with_variable_id(
1161                VariableId::global("x".to_string()),
1162                Expr::number(BigDecimal::from(1)),
1163                Some(TypeName::U32),
1164            ),
1165            Expr::let_binding_with_variable_id(
1166                VariableId::global("y".to_string()),
1167                Expr::number(BigDecimal::from(2)),
1168                Some(TypeName::U32),
1169            ),
1170        ]);
1171        let expr_str = to_string(&input_expr).unwrap();
1172        let expected_str = "let x: u32 = 1;\nlet y: u32 = 2".to_string();
1173        let output_expr = from_string(expr_str.as_str()).unwrap();
1174        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1175    }
1176
1177    #[test]
1178    fn test_round_trip_read_write_let_with_type_binding_option() {
1179        let input_expr = Expr::expr_block(vec![
1180            Expr::let_binding_with_variable_id(
1181                VariableId::global("x".to_string()),
1182                Expr::option(Some(Expr::literal("foo")))
1183                    .with_inferred_type(InferredType::option(InferredType::string())),
1184                Some(TypeName::Option(Box::new(TypeName::Str))),
1185            ),
1186            Expr::let_binding_with_variable_id(
1187                VariableId::global("y".to_string()),
1188                Expr::option(Some(Expr::literal("bar")))
1189                    .with_inferred_type(InferredType::option(InferredType::string())),
1190                Some(TypeName::Option(Box::new(TypeName::Str))),
1191            ),
1192        ]);
1193        let expr_str = to_string(&input_expr).unwrap();
1194        let expected_str =
1195            "let x: option<string> = some(\"foo\");\nlet y: option<string> = some(\"bar\")"
1196                .to_string();
1197        let output_expr = from_string(expr_str.as_str()).unwrap();
1198        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1199    }
1200
1201    #[test]
1202    fn test_round_trip_read_write_let_with_type_binding_list() {
1203        let input_expr = Expr::expr_block(vec![
1204            Expr::let_binding_with_variable_id(
1205                VariableId::global("x".to_string()),
1206                Expr::sequence(vec![Expr::literal("foo")], None)
1207                    .with_inferred_type(InferredType::list(InferredType::string())),
1208                Some(TypeName::List(Box::new(TypeName::Str))),
1209            ),
1210            Expr::let_binding_with_variable_id(
1211                VariableId::global("y".to_string()),
1212                Expr::sequence(vec![Expr::literal("bar")], None)
1213                    .with_inferred_type(InferredType::list(InferredType::string())),
1214                Some(TypeName::List(Box::new(TypeName::Str))),
1215            ),
1216        ]);
1217        let expr_str = to_string(&input_expr).unwrap();
1218        let expected_str =
1219            "let x: list<string> = [\"foo\"];\nlet y: list<string> = [\"bar\"]".to_string();
1220        let output_expr = from_string(expr_str.as_str()).unwrap();
1221        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1222    }
1223
1224    #[test]
1225    fn test_round_trip_read_write_let_with_type_binding_tuple() {
1226        let input_expr = Expr::expr_block(vec![
1227            Expr::let_binding_with_variable_id(
1228                VariableId::global("x".to_string()),
1229                Expr::tuple(vec![Expr::literal("foo")])
1230                    .with_inferred_type(InferredType::tuple(vec![InferredType::string()])),
1231                Some(TypeName::Tuple(vec![TypeName::Str])),
1232            ),
1233            Expr::let_binding_with_variable_id(
1234                VariableId::global("y".to_string()),
1235                Expr::tuple(vec![Expr::literal("bar")])
1236                    .with_inferred_type(InferredType::tuple(vec![InferredType::string()])),
1237                Some(TypeName::Tuple(vec![TypeName::Str])),
1238            ),
1239        ]);
1240        let expr_str = to_string(&input_expr).unwrap();
1241        let expected_str =
1242            "let x: tuple<string> = (\"foo\");\nlet y: tuple<string> = (\"bar\")".to_string();
1243        let output_expr = from_string(expr_str.as_str()).unwrap();
1244        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1245    }
1246}
1247
1248#[cfg(test)]
1249mod selection_tests {
1250    use bigdecimal::BigDecimal;
1251    use test_r::test;
1252
1253    use crate::expr::Expr;
1254    use crate::text::{from_string, to_string};
1255
1256    #[test]
1257    fn test_round_trip_read_write_select_field_from_request() {
1258        let input_expr =
1259            Expr::select_field(Expr::identifier_global("request", None), "field", None);
1260        let expr_str = to_string(&input_expr).unwrap();
1261        let expected_str = "request.field".to_string();
1262        let output_expr = from_string(expr_str.as_str()).unwrap();
1263        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1264    }
1265
1266    #[test]
1267    fn test_round_trip_read_write_select_index_from_request() {
1268        let input_expr = Expr::select_index(
1269            Expr::identifier_global("request", None),
1270            Expr::number(BigDecimal::from(1)),
1271        );
1272        let expr_str = to_string(&input_expr).unwrap();
1273        let expected_str = "request[1]".to_string();
1274        let output_expr = from_string(expr_str.as_str()).unwrap();
1275        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1276    }
1277
1278    #[test]
1279    fn test_round_trip_read_write_select_field_from_record() {
1280        let input_expr = Expr::select_field(
1281            Expr::record(vec![(
1282                "field".to_string(),
1283                Expr::identifier_global("request", None),
1284            )]),
1285            "field",
1286            None,
1287        );
1288        let expr_str = to_string(&input_expr).unwrap();
1289        let expected_str = "{field: request}.field".to_string();
1290        let output_expr = from_string(expr_str.as_str()).unwrap();
1291        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1292    }
1293
1294    #[test]
1295    fn test_round_trip_read_write_select_index_from_sequence() {
1296        let input_expr = Expr::select_index(
1297            Expr::sequence(
1298                vec![
1299                    Expr::identifier_global("request", None),
1300                    Expr::identifier_global("request", None),
1301                ],
1302                None,
1303            ),
1304            Expr::number(BigDecimal::from(1)),
1305        );
1306        let expr_str = to_string(&input_expr).unwrap();
1307        let expected_str = "[request, request][1]".to_string();
1308        let output_expr = from_string(expr_str.as_str()).unwrap();
1309        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1310    }
1311}
1312
1313#[cfg(test)]
1314mod flag_tests {
1315    use test_r::test;
1316
1317    use crate::expr::Expr;
1318    use crate::text::{from_string, to_string};
1319
1320    #[test]
1321    fn test_round_trip_read_write_flags_single() {
1322        let input_expr = Expr::flags(vec!["flag1".to_string()]);
1323        let expr_str = to_string(&input_expr).unwrap();
1324        let expected_str = "{flag1}".to_string();
1325        let output_expr = from_string(expr_str.as_str()).unwrap();
1326        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1327    }
1328
1329    #[test]
1330    fn test_round_trip_read_write_flags() {
1331        let input_expr = Expr::flags(vec![
1332            "flag1".to_string(),
1333            "flag2".to_string(),
1334            "flag3".to_string(),
1335        ]);
1336        let expr_str = to_string(&input_expr).unwrap();
1337        let expected_str = "{flag1, flag2, flag3}".to_string();
1338        let output_expr = from_string(expr_str.as_str()).unwrap();
1339        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1340    }
1341}
1342
1343#[cfg(test)]
1344mod match_tests {
1345    use bigdecimal::BigDecimal;
1346    use std::str::FromStr;
1347    use test_r::test;
1348
1349    use crate::expr::ArmPattern;
1350    use crate::expr::Expr;
1351    use crate::expr::MatchArm;
1352    use crate::text::{from_string, to_string};
1353
1354    #[test]
1355    fn test_round_trip_match_expr() {
1356        let mut input_expr = Expr::pattern_match(
1357            Expr::identifier_global("request", None),
1358            vec![
1359                MatchArm::new(
1360                    ArmPattern::constructor(
1361                        "ok",
1362                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1363                    ),
1364                    Expr::literal("success"),
1365                ),
1366                MatchArm::new(
1367                    ArmPattern::constructor(
1368                        "err",
1369                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1370                    ),
1371                    Expr::literal("failure"),
1372                ),
1373            ],
1374        );
1375
1376        input_expr.reset_type();
1377
1378        let expr_str = to_string(&input_expr).unwrap();
1379        let expected_str =
1380            r#"match request {  ok(foo) => "success", err(msg) => "failure" } "#.to_string();
1381        let mut output_expr = from_string(expr_str.as_str()).unwrap();
1382        output_expr.reset_type();
1383        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1384    }
1385
1386    #[test]
1387    fn test_round_trip_match_expr_of_flags() {
1388        let input_expr = Expr::pattern_match(
1389            Expr::identifier_global("request", None),
1390            vec![
1391                MatchArm::new(
1392                    ArmPattern::constructor(
1393                        "ok",
1394                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1395                    ),
1396                    Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]),
1397                ),
1398                MatchArm::new(
1399                    ArmPattern::constructor(
1400                        "err",
1401                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1402                    ),
1403                    Expr::literal("failure"),
1404                ),
1405            ],
1406        );
1407
1408        let expr_str = to_string(&input_expr).unwrap();
1409        let expected_str =
1410            r#"match request {  ok(foo) => {flag1, flag2}, err(msg) => "failure" } "#.to_string();
1411        let output_expr = from_string(expr_str.as_str()).unwrap();
1412        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1413    }
1414
1415    #[test]
1416    fn test_round_trip_match_expr_of_tuple() {
1417        let input_expr = Expr::pattern_match(
1418            Expr::identifier_global("request", None),
1419            vec![
1420                MatchArm::new(
1421                    ArmPattern::constructor(
1422                        "ok",
1423                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1424                    ),
1425                    Expr::tuple(vec![
1426                        Expr::identifier_global("request", None),
1427                        Expr::identifier_global("request", None),
1428                    ]),
1429                ),
1430                MatchArm::new(
1431                    ArmPattern::constructor(
1432                        "err",
1433                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1434                    ),
1435                    Expr::literal("failure"),
1436                ),
1437            ],
1438        );
1439
1440        let expr_str = to_string(&input_expr).unwrap();
1441        let expected_str =
1442            r#"match request {  ok(foo) => (request, request), err(msg) => "failure" } "#
1443                .to_string();
1444        let output_expr = from_string(expr_str.as_str()).unwrap();
1445        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1446    }
1447
1448    #[test]
1449    fn test_round_trip_match_expr_of_sequence() {
1450        let input_expr = Expr::pattern_match(
1451            Expr::identifier_global("request", None),
1452            vec![
1453                MatchArm::new(
1454                    ArmPattern::constructor(
1455                        "ok",
1456                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1457                    ),
1458                    Expr::sequence(
1459                        vec![
1460                            Expr::identifier_global("request", None),
1461                            Expr::identifier_global("request", None),
1462                        ],
1463                        None,
1464                    ),
1465                ),
1466                MatchArm::new(
1467                    ArmPattern::constructor(
1468                        "err",
1469                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1470                    ),
1471                    Expr::literal("failure"),
1472                ),
1473            ],
1474        );
1475
1476        let expr_str = to_string(&input_expr).unwrap();
1477        let expected_str =
1478            r#"match request {  ok(foo) => [request, request], err(msg) => "failure" } "#
1479                .to_string();
1480        let output_expr = from_string(expr_str.as_str()).unwrap();
1481        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1482    }
1483
1484    #[test]
1485    fn test_round_trip_match_expr_of_record() {
1486        let input_expr = Expr::pattern_match(
1487            Expr::identifier_global("request", None),
1488            vec![
1489                MatchArm::new(
1490                    ArmPattern::constructor(
1491                        "ok",
1492                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1493                    ),
1494                    Expr::record(vec![(
1495                        "field".to_string(),
1496                        Expr::identifier_global("request", None),
1497                    )]),
1498                ),
1499                MatchArm::new(
1500                    ArmPattern::constructor(
1501                        "err",
1502                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1503                    ),
1504                    Expr::literal("failure"),
1505                ),
1506            ],
1507        );
1508
1509        let expr_str = to_string(&input_expr).unwrap();
1510        let expected_str =
1511            r#"match request {  ok(foo) => {field: request}, err(msg) => "failure" } "#.to_string();
1512        let output_expr = from_string(expr_str.as_str()).unwrap();
1513        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1514    }
1515
1516    #[test]
1517    fn test_round_trip_match_expr_of_math_op() {
1518        let input_expr = Expr::pattern_match(
1519            Expr::identifier_global("request", None),
1520            vec![
1521                MatchArm::new(
1522                    ArmPattern::constructor(
1523                        "ok",
1524                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1525                    ),
1526                    Expr::greater_than(
1527                        Expr::number(BigDecimal::from_str("1.1").unwrap()),
1528                        Expr::number(BigDecimal::from(2)),
1529                    ),
1530                ),
1531                MatchArm::new(
1532                    ArmPattern::constructor(
1533                        "err",
1534                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1535                    ),
1536                    Expr::less_than(
1537                        Expr::number(BigDecimal::from(1)),
1538                        Expr::number(BigDecimal::from(2)),
1539                    ),
1540                ),
1541            ],
1542        );
1543
1544        let expr_str = to_string(&input_expr).unwrap();
1545        let expected_str = "match request {  ok(foo) => 1.1 > 2, err(msg) => 1 < 2 } ".to_string();
1546        let output_expr = from_string(expr_str.as_str()).unwrap();
1547        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1548    }
1549
1550    #[test]
1551    fn test_round_trip_match_expr_of_if_condition() {
1552        let input_expr = Expr::pattern_match(
1553            Expr::identifier_global("request", None),
1554            vec![
1555                MatchArm::new(
1556                    ArmPattern::constructor(
1557                        "ok",
1558                        vec![ArmPattern::literal(Expr::identifier_global("foo", None))],
1559                    ),
1560                    Expr::cond(
1561                        Expr::equal_to(
1562                            Expr::select_field(
1563                                Expr::identifier_global("request", None),
1564                                "foo",
1565                                None,
1566                            ),
1567                            Expr::literal("bar"),
1568                        ),
1569                        Expr::literal("success"),
1570                        Expr::literal("failed"),
1571                    ),
1572                ),
1573                MatchArm::new(
1574                    ArmPattern::constructor(
1575                        "err",
1576                        vec![ArmPattern::literal(Expr::identifier_global("msg", None))],
1577                    ),
1578                    Expr::literal("failure"),
1579                ),
1580            ],
1581        );
1582
1583        let expr_str = to_string(&input_expr).unwrap();
1584        let expected_str =
1585            r#"match request {  ok(foo) => if request.foo == "bar" then "success" else "failed", err(msg) => "failure" } "#.to_string();
1586        let output_expr = from_string(expr_str.as_str()).unwrap();
1587        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1588    }
1589
1590    #[test]
1591    fn test_pattern_match_multiple_constructor_variables() {
1592        let input_expr = Expr::pattern_match(
1593            Expr::identifier_global("request", None),
1594            vec![
1595                MatchArm::new(
1596                    ArmPattern::custom_constructor(
1597                        "foo",
1598                        vec![ArmPattern::identifier("a"), ArmPattern::identifier("b")],
1599                    ),
1600                    Expr::literal("success"),
1601                ),
1602                MatchArm::new(
1603                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1604                    Expr::literal("failure"),
1605                ),
1606            ],
1607        );
1608
1609        let expr_str = to_string(&input_expr).unwrap();
1610        let expected_str =
1611            r#"match request {  foo(a,b) => "success", bar(c) => "failure" } "#.to_string();
1612        let output_expr = from_string(expr_str.as_str()).unwrap();
1613        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1614    }
1615
1616    #[test]
1617    fn test_pattern_match_empty_constructor_variables() {
1618        let input_expr = Expr::pattern_match(
1619            Expr::identifier_global("request", None),
1620            vec![
1621                MatchArm::new(ArmPattern::identifier("foo"), Expr::literal("success")),
1622                MatchArm::new(
1623                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1624                    Expr::literal("failure"),
1625                ),
1626            ],
1627        );
1628
1629        let expr_str = to_string(&input_expr).unwrap();
1630        let expected_str =
1631            r#"match request {  foo => "success", bar(c) => "failure" } "#.to_string();
1632        let output_expr = from_string(expr_str.as_str()).unwrap();
1633        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1634    }
1635
1636    #[test]
1637    fn test_pattern_match_empty_with_nested_constructor_patterns() {
1638        let input_expr = Expr::pattern_match(
1639            Expr::identifier_global("request", None),
1640            vec![
1641                MatchArm::new(
1642                    ArmPattern::custom_constructor(
1643                        "foo",
1644                        vec![ArmPattern::custom_constructor(
1645                            "bar",
1646                            vec![ArmPattern::identifier("v1")],
1647                        )],
1648                    ),
1649                    Expr::literal("success"),
1650                ),
1651                MatchArm::new(
1652                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1653                    Expr::literal("failure"),
1654                ),
1655            ],
1656        );
1657
1658        let expr_str = to_string(&input_expr).unwrap();
1659        let expected_str =
1660            r#"match request {  foo(bar(v1)) => "success", bar(c) => "failure" } "#.to_string();
1661        let output_expr = from_string(expr_str.as_str()).unwrap();
1662        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1663    }
1664
1665    #[test]
1666    fn test_pattern_match_variants_in_arm_rhs() {
1667        let input_expr = Expr::pattern_match(
1668            Expr::identifier_global("request", None),
1669            vec![
1670                MatchArm::new(
1671                    ArmPattern::identifier("foo1"),
1672                    Expr::ok(Expr::literal("foo"), None),
1673                ),
1674                MatchArm::new(
1675                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1676                    Expr::err(Expr::literal("bar"), None),
1677                ),
1678            ],
1679        );
1680
1681        let expr_str = to_string(&input_expr).unwrap();
1682        let expected_str =
1683            r#"match request {  foo1 => ok("foo"), bar(c) => err("bar") } "#.to_string();
1684        let output_expr = from_string(expr_str.as_str()).unwrap();
1685        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1686    }
1687
1688    #[test]
1689    fn test_pattern_match_variants_in_wild_pattern() {
1690        let input_expr = Expr::pattern_match(
1691            Expr::identifier_global("request", None),
1692            vec![
1693                MatchArm::new(
1694                    ArmPattern::custom_constructor("foo1", vec![ArmPattern::WildCard]),
1695                    Expr::ok(Expr::literal("foo"), None),
1696                ),
1697                MatchArm::new(
1698                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1699                    Expr::err(Expr::literal("bar"), None),
1700                ),
1701            ],
1702        );
1703
1704        let expr_str = to_string(&input_expr).unwrap();
1705        let expected_str =
1706            r#"match request {  foo1(_) => ok("foo"), bar(c) => err("bar") } "#.to_string();
1707        let output_expr = from_string(expr_str.as_str()).unwrap();
1708        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1709    }
1710
1711    #[test]
1712    fn test_pattern_match_variants_with_alias() {
1713        let input_expr = Expr::pattern_match(
1714            Expr::identifier_global("request", None),
1715            vec![
1716                MatchArm::new(
1717                    ArmPattern::As(
1718                        "name".to_string(),
1719                        Box::new(ArmPattern::custom_constructor(
1720                            "foo1",
1721                            vec![ArmPattern::WildCard],
1722                        )),
1723                    ),
1724                    Expr::ok(Expr::literal("foo"), None),
1725                ),
1726                MatchArm::new(
1727                    ArmPattern::custom_constructor("bar", vec![ArmPattern::identifier("c")]),
1728                    Expr::err(Expr::literal("bar"), None),
1729                ),
1730            ],
1731        );
1732
1733        let expr_str = to_string(&input_expr).unwrap();
1734        let expected_str =
1735            r#"match request {  name @ foo1(_) => ok("foo"), bar(c) => err("bar") } "#.to_string();
1736        let output_expr = from_string(expr_str.as_str()).unwrap();
1737        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1738    }
1739
1740    #[test]
1741    fn test_pattern_match_variants_with_nested_alias() {
1742        let input_expr = Expr::pattern_match(
1743            Expr::identifier_global("request", None),
1744            vec![
1745                MatchArm::new(
1746                    ArmPattern::As(
1747                        "a".to_string(),
1748                        Box::new(ArmPattern::custom_constructor(
1749                            "foo",
1750                            vec![ArmPattern::As(
1751                                "b".to_string(),
1752                                Box::new(ArmPattern::WildCard),
1753                            )],
1754                        )),
1755                    ),
1756                    Expr::ok(Expr::literal("foo"), None),
1757                ),
1758                MatchArm::new(
1759                    ArmPattern::As(
1760                        "c".to_string(),
1761                        Box::new(ArmPattern::custom_constructor(
1762                            "bar",
1763                            vec![ArmPattern::As(
1764                                "d".to_string(),
1765                                Box::new(ArmPattern::custom_constructor(
1766                                    "baz",
1767                                    vec![ArmPattern::identifier("x")],
1768                                )),
1769                            )],
1770                        )),
1771                    ),
1772                    Expr::err(Expr::literal("bar"), None),
1773                ),
1774            ],
1775        );
1776
1777        let expr_str = to_string(&input_expr).unwrap();
1778        let expected_str =
1779            r#"match request {  a @ foo(b @ _) => ok("foo"), c @ bar(d @ baz(x)) => err("bar") } "#
1780                .to_string();
1781        let output_expr = from_string(expr_str.as_str()).unwrap();
1782        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1783    }
1784}
1785
1786#[cfg(test)]
1787mod if_cond_tests {
1788    use bigdecimal::BigDecimal;
1789    use test_r::test;
1790
1791    use crate::expr::Expr;
1792    use crate::text::{from_string, to_string};
1793
1794    #[test]
1795    fn test_round_trip_if_condition_literals() {
1796        let input_expr = Expr::cond(
1797            Expr::equal_to(Expr::literal("foo"), Expr::literal("bar")),
1798            Expr::literal("success"),
1799            Expr::literal("failed"),
1800        );
1801
1802        let expr_str = to_string(&input_expr).unwrap();
1803        let expected_str = r#"if "foo" == "bar" then "success" else "failed""#.to_string();
1804        let output_expr = from_string(expected_str.as_str()).unwrap();
1805        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1806    }
1807
1808    #[test]
1809    fn test_round_trip_if_condition_of_select_field() {
1810        let input_expr = Expr::cond(
1811            Expr::equal_to(
1812                Expr::select_field(Expr::identifier_global("request", None), "foo", None),
1813                Expr::literal("bar"),
1814            ),
1815            Expr::literal("success"),
1816            Expr::literal("failed"),
1817        );
1818
1819        let expr_str = to_string(&input_expr).unwrap();
1820        let expected_str = r#"if request.foo == "bar" then "success" else "failed""#.to_string();
1821        let output_expr = from_string(expr_str.as_str()).unwrap();
1822        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1823    }
1824
1825    #[test]
1826    fn test_round_trip_nested_if_condition() {
1827        let input_expr = Expr::cond(
1828            Expr::equal_to(
1829                Expr::select_field(Expr::identifier_global("request", None), "foo", None),
1830                Expr::literal("bar"),
1831            ),
1832            Expr::literal("success"),
1833            Expr::cond(
1834                Expr::equal_to(
1835                    Expr::select_field(Expr::identifier_global("request", None), "foo", None),
1836                    Expr::literal("baz"),
1837                ),
1838                Expr::literal("success"),
1839                Expr::literal("failed"),
1840            ),
1841        );
1842
1843        let expr_str = to_string(&input_expr).unwrap();
1844        let expected_str = r#"if request.foo == "bar" then "success" else if request.foo == "baz" then "success" else "failed""#.to_string();
1845        let output_expr = from_string(expr_str.as_str()).unwrap();
1846        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1847    }
1848
1849    #[test]
1850    fn test_round_trip_if_condition_of_tuple() {
1851        let input_expr = Expr::cond(
1852            Expr::equal_to(
1853                Expr::identifier_global("foo", None),
1854                Expr::identifier_global("bar", None),
1855            ),
1856            Expr::tuple(vec![
1857                Expr::identifier_global("foo", None),
1858                Expr::identifier_global("bar", None),
1859            ]),
1860            Expr::tuple(vec![
1861                Expr::identifier_global("request", None),
1862                Expr::identifier_global("request", None),
1863            ]),
1864        );
1865
1866        let expr_str = to_string(&input_expr).unwrap();
1867        let expected_str = r#"if foo == bar then (foo, bar) else (request, request)"#.to_string();
1868        let output_expr = from_string(expr_str.as_str()).unwrap();
1869        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1870    }
1871
1872    #[test]
1873    fn test_round_trip_if_condition_of_sequence() {
1874        let input_expr = Expr::cond(
1875            Expr::equal_to(
1876                Expr::identifier_global("foo", None),
1877                Expr::identifier_global("bar", None),
1878            ),
1879            Expr::sequence(
1880                vec![
1881                    Expr::identifier_global("request", None),
1882                    Expr::identifier_global("request", None),
1883                ],
1884                None,
1885            ),
1886            Expr::sequence(
1887                vec![
1888                    Expr::identifier_global("foo", None),
1889                    Expr::identifier_global("bar", None),
1890                ],
1891                None,
1892            ),
1893        );
1894
1895        let expr_str = to_string(&input_expr).unwrap();
1896        let expected_str = r#"if foo == bar then [request, request] else [foo, bar]"#.to_string();
1897        let output_expr = from_string(expr_str.as_str()).unwrap();
1898        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1899    }
1900
1901    #[test]
1902    fn test_round_trip_if_condition_of_record() {
1903        let input_expr = Expr::cond(
1904            Expr::equal_to(
1905                Expr::identifier_global("field1", None),
1906                Expr::identifier_global("field2", None),
1907            ),
1908            Expr::record(vec![(
1909                "field".to_string(),
1910                Expr::identifier_global("request", None),
1911            )]),
1912            Expr::literal("failed"),
1913        );
1914
1915        let expr_str = to_string(&input_expr).unwrap();
1916        let expected_str = r#"if field1 == field2 then {field: request} else "failed""#.to_string();
1917        let output_expr = from_string(expr_str.as_str()).unwrap();
1918        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1919    }
1920
1921    #[test]
1922    fn test_round_trip_if_condition_of_flags() {
1923        let input_expr = Expr::cond(
1924            Expr::equal_to(
1925                Expr::select_field(Expr::identifier_global("worker", None), "response", None),
1926                Expr::number(BigDecimal::from(1)),
1927            ),
1928            Expr::flags(vec!["flag1".to_string(), "flag2".to_string()]),
1929            Expr::literal("failed"),
1930        );
1931
1932        let expr_str = to_string(&input_expr).unwrap();
1933        let expected_str =
1934            r#"if worker.response == 1 then {flag1, flag2} else "failed""#.to_string();
1935        let output_expr = from_string(expr_str.as_str()).unwrap();
1936        assert_eq!((expr_str, input_expr), (expected_str, output_expr));
1937    }
1938}