rib/text/
mod.rs

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