1use super::FromJsonError;
18#[cfg(feature = "tolerant-ast")]
19use crate::ast::expr_allows_errors::AstExprErrorKind;
20#[cfg(feature = "tolerant-ast")]
21use crate::ast::Infallible;
22use crate::ast::{self, BoundedDisplay, EntityUID};
23use crate::entities::json::{
24 err::EscapeKind, err::JsonDeserializationError, err::JsonDeserializationErrorContext,
25 CedarValueJson, FnAndArg,
26};
27use crate::expr_builder::ExprBuilder;
28use crate::extensions::Extensions;
29use crate::jsonvalue::JsonValueWithNoDuplicateKeys;
30use crate::parser::cst_to_ast;
31use crate::parser::err::ParseErrors;
32#[cfg(feature = "tolerant-ast")]
33use crate::parser::err::{ToASTError, ToASTErrorKind};
34use crate::parser::Node;
35use crate::parser::{cst, Loc};
36use itertools::Itertools;
37use serde::{de::Visitor, Deserialize, Serialize};
38use serde_with::serde_as;
39use smol_str::{SmolStr, ToSmolStr};
40use std::collections::{btree_map, BTreeMap, HashMap};
41use std::sync::Arc;
42
43#[derive(Debug, Clone, PartialEq, Serialize)]
45#[serde(untagged)]
46#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
47#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
48pub enum Expr {
49 ExprNoExt(ExprNoExt),
51 ExtFuncCall(ExtFuncCall),
54}
55
56impl<'de> Deserialize<'de> for Expr {
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: serde::Deserializer<'de>,
69 {
70 struct ExprVisitor;
71 impl<'de> Visitor<'de> for ExprVisitor {
72 type Value = Expr;
73 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 formatter.write_str("JSON object representing an expression")
75 }
76
77 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
78 where
79 A: serde::de::MapAccess<'de>,
80 {
81 let (k, v): (SmolStr, JsonValueWithNoDuplicateKeys) = match map.next_entry()? {
82 None => {
83 return Err(serde::de::Error::custom(
84 "empty map is not a valid expression",
85 ))
86 }
87 Some((k, v)) => (k, v),
88 };
89 match map.next_key()? {
90 None => (),
91 Some(k2) => {
92 let k2: SmolStr = k2;
93 return Err(serde::de::Error::custom(format!("JSON object representing an `Expr` should have only one key, but found two keys: `{k}` and `{k2}`")));
94 }
95 };
96 if cst_to_ast::is_known_extension_func_str(&k) {
97 let obj = serde_json::json!({ k: v });
101 let extfunccall =
102 serde_json::from_value(obj).map_err(serde::de::Error::custom)?;
103 Ok(Expr::ExtFuncCall(extfunccall))
104 } else {
105 let obj = serde_json::json!({ k: v });
108 let exprnoext =
109 serde_json::from_value(obj).map_err(serde::de::Error::custom)?;
110 Ok(Expr::ExprNoExt(exprnoext))
111 }
112 }
113 }
114
115 deserializer.deserialize_map(ExprVisitor)
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
122#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
123pub enum PatternElem {
124 Wildcard,
126 Literal(SmolStr),
128}
129
130impl From<&[PatternElem]> for crate::ast::Pattern {
131 fn from(value: &[PatternElem]) -> Self {
132 let mut elems = Vec::new();
133 for elem in value {
134 match elem {
135 PatternElem::Wildcard => {
136 elems.push(crate::ast::PatternElem::Wildcard);
137 }
138 PatternElem::Literal(s) => {
139 elems.extend(s.chars().map(crate::ast::PatternElem::Char));
140 }
141 }
142 }
143 Self::from(elems)
144 }
145}
146
147impl From<crate::ast::PatternElem> for PatternElem {
148 fn from(value: crate::ast::PatternElem) -> Self {
149 match value {
150 crate::ast::PatternElem::Wildcard => Self::Wildcard,
151 crate::ast::PatternElem::Char(c) => Self::Literal(c.to_smolstr()),
152 }
153 }
154}
155
156impl From<crate::ast::Pattern> for Vec<PatternElem> {
157 fn from(value: crate::ast::Pattern) -> Self {
158 value.iter().map(|elem| (*elem).into()).collect()
159 }
160}
161
162#[serde_as]
165#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
166#[serde(deny_unknown_fields)]
167#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
168#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
169pub enum ExprNoExt {
170 Value(CedarValueJson),
173 Var(ast::Var),
175 Slot(#[cfg_attr(feature = "wasm", tsify(type = "string"))] ast::SlotId),
177 #[serde(rename = "!")]
179 Not {
180 arg: Arc<Expr>,
182 },
183 #[serde(rename = "neg")]
185 Neg {
186 arg: Arc<Expr>,
188 },
189 #[serde(rename = "==")]
191 Eq {
192 left: Arc<Expr>,
194 right: Arc<Expr>,
196 },
197 #[serde(rename = "!=")]
199 NotEq {
200 left: Arc<Expr>,
202 right: Arc<Expr>,
204 },
205 #[serde(rename = "in")]
207 In {
208 left: Arc<Expr>,
210 right: Arc<Expr>,
212 },
213 #[serde(rename = "<")]
215 Less {
216 left: Arc<Expr>,
218 right: Arc<Expr>,
220 },
221 #[serde(rename = "<=")]
223 LessEq {
224 left: Arc<Expr>,
226 right: Arc<Expr>,
228 },
229 #[serde(rename = ">")]
231 Greater {
232 left: Arc<Expr>,
234 right: Arc<Expr>,
236 },
237 #[serde(rename = ">=")]
239 GreaterEq {
240 left: Arc<Expr>,
242 right: Arc<Expr>,
244 },
245 #[serde(rename = "&&")]
247 And {
248 left: Arc<Expr>,
250 right: Arc<Expr>,
252 },
253 #[serde(rename = "||")]
255 Or {
256 left: Arc<Expr>,
258 right: Arc<Expr>,
260 },
261 #[serde(rename = "+")]
263 Add {
264 left: Arc<Expr>,
266 right: Arc<Expr>,
268 },
269 #[serde(rename = "-")]
271 Sub {
272 left: Arc<Expr>,
274 right: Arc<Expr>,
276 },
277 #[serde(rename = "*")]
279 Mul {
280 left: Arc<Expr>,
282 right: Arc<Expr>,
284 },
285 #[serde(rename = "contains")]
287 Contains {
288 left: Arc<Expr>,
290 right: Arc<Expr>,
292 },
293 #[serde(rename = "containsAll")]
295 ContainsAll {
296 left: Arc<Expr>,
298 right: Arc<Expr>,
300 },
301 #[serde(rename = "containsAny")]
303 ContainsAny {
304 left: Arc<Expr>,
306 right: Arc<Expr>,
308 },
309 #[serde(rename = "isEmpty")]
311 IsEmpty {
312 arg: Arc<Expr>,
314 },
315 #[serde(rename = "getTag")]
317 GetTag {
318 left: Arc<Expr>,
320 right: Arc<Expr>,
322 },
323 #[serde(rename = "hasTag")]
325 HasTag {
326 left: Arc<Expr>,
328 right: Arc<Expr>,
330 },
331 #[serde(rename = ".")]
333 GetAttr {
334 left: Arc<Expr>,
336 attr: SmolStr,
338 },
339 #[serde(rename = "has")]
341 HasAttr {
342 left: Arc<Expr>,
344 attr: SmolStr,
346 },
347 #[serde(rename = "like")]
349 Like {
350 left: Arc<Expr>,
352 pattern: Vec<PatternElem>,
354 },
355 #[serde(rename = "is")]
357 Is {
358 left: Arc<Expr>,
360 entity_type: SmolStr,
362 #[serde(skip_serializing_if = "Option::is_none")]
364 #[serde(rename = "in")]
365 in_expr: Option<Arc<Expr>>,
366 },
367 #[serde(rename = "if-then-else")]
369 If {
370 #[serde(rename = "if")]
372 cond_expr: Arc<Expr>,
373 #[serde(rename = "then")]
375 then_expr: Arc<Expr>,
376 #[serde(rename = "else")]
378 else_expr: Arc<Expr>,
379 },
380 Set(Vec<Expr>),
384 Record(
388 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
389 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, Expr>"))]
390 BTreeMap<SmolStr, Expr>,
391 ),
392 #[cfg(feature = "tolerant-ast")]
394 Error(AstExprErrorKind),
395}
396
397#[serde_as]
399#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
400#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
401#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
402pub struct ExtFuncCall {
403 #[serde(flatten)]
412 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
413 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, Array<Expr>>"))]
414 call: HashMap<SmolStr, Vec<Expr>>,
415}
416
417#[derive(Clone, Debug)]
419pub struct Builder;
420
421impl ExprBuilder for Builder {
422 type Expr = Expr;
423
424 type Data = ();
425 #[cfg(feature = "tolerant-ast")]
426 type ErrorType = Infallible;
427
428 fn with_data(_data: Self::Data) -> Self {
429 Self
430 }
431
432 fn with_maybe_source_loc(self, _: Option<&Loc>) -> Self {
433 self
434 }
435
436 fn loc(&self) -> Option<&Loc> {
437 None
438 }
439
440 fn data(&self) -> &Self::Data {
441 &()
442 }
443
444 fn val(self, lit: impl Into<ast::Literal>) -> Expr {
446 Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::from_lit(lit.into())))
447 }
448
449 fn var(self, var: ast::Var) -> Expr {
451 Expr::ExprNoExt(ExprNoExt::Var(var))
452 }
453
454 fn slot(self, slot: ast::SlotId) -> Expr {
456 Expr::ExprNoExt(ExprNoExt::Slot(slot))
457 }
458
459 fn unknown(self, u: ast::Unknown) -> Expr {
461 Expr::ExtFuncCall(ExtFuncCall {
462 call: HashMap::from([("unknown".to_smolstr(), vec![Builder::new().val(u.name)])]),
463 })
464 }
465
466 fn not(self, e: Expr) -> Expr {
468 Expr::ExprNoExt(ExprNoExt::Not { arg: Arc::new(e) })
469 }
470
471 fn neg(self, e: Expr) -> Expr {
473 Expr::ExprNoExt(ExprNoExt::Neg { arg: Arc::new(e) })
474 }
475
476 fn is_eq(self, left: Expr, right: Expr) -> Expr {
478 Expr::ExprNoExt(ExprNoExt::Eq {
479 left: Arc::new(left),
480 right: Arc::new(right),
481 })
482 }
483
484 fn noteq(self, left: Expr, right: Expr) -> Expr {
486 Expr::ExprNoExt(ExprNoExt::NotEq {
487 left: Arc::new(left),
488 right: Arc::new(right),
489 })
490 }
491
492 fn is_in(self, left: Expr, right: Expr) -> Expr {
494 Expr::ExprNoExt(ExprNoExt::In {
495 left: Arc::new(left),
496 right: Arc::new(right),
497 })
498 }
499
500 fn less(self, left: Expr, right: Expr) -> Expr {
502 Expr::ExprNoExt(ExprNoExt::Less {
503 left: Arc::new(left),
504 right: Arc::new(right),
505 })
506 }
507
508 fn lesseq(self, left: Expr, right: Expr) -> Expr {
510 Expr::ExprNoExt(ExprNoExt::LessEq {
511 left: Arc::new(left),
512 right: Arc::new(right),
513 })
514 }
515
516 fn greater(self, left: Expr, right: Expr) -> Expr {
518 Expr::ExprNoExt(ExprNoExt::Greater {
519 left: Arc::new(left),
520 right: Arc::new(right),
521 })
522 }
523
524 fn greatereq(self, left: Expr, right: Expr) -> Expr {
526 Expr::ExprNoExt(ExprNoExt::GreaterEq {
527 left: Arc::new(left),
528 right: Arc::new(right),
529 })
530 }
531
532 fn and(self, left: Expr, right: Expr) -> Expr {
534 Expr::ExprNoExt(ExprNoExt::And {
535 left: Arc::new(left),
536 right: Arc::new(right),
537 })
538 }
539
540 fn or(self, left: Expr, right: Expr) -> Expr {
542 Expr::ExprNoExt(ExprNoExt::Or {
543 left: Arc::new(left),
544 right: Arc::new(right),
545 })
546 }
547
548 fn add(self, left: Expr, right: Expr) -> Expr {
550 Expr::ExprNoExt(ExprNoExt::Add {
551 left: Arc::new(left),
552 right: Arc::new(right),
553 })
554 }
555
556 fn sub(self, left: Expr, right: Expr) -> Expr {
558 Expr::ExprNoExt(ExprNoExt::Sub {
559 left: Arc::new(left),
560 right: Arc::new(right),
561 })
562 }
563
564 fn mul(self, left: Expr, right: Expr) -> Expr {
566 Expr::ExprNoExt(ExprNoExt::Mul {
567 left: Arc::new(left),
568 right: Arc::new(right),
569 })
570 }
571
572 fn contains(self, left: Expr, right: Expr) -> Expr {
574 Expr::ExprNoExt(ExprNoExt::Contains {
575 left: Arc::new(left),
576 right: Arc::new(right),
577 })
578 }
579
580 fn contains_all(self, left: Expr, right: Expr) -> Expr {
582 Expr::ExprNoExt(ExprNoExt::ContainsAll {
583 left: Arc::new(left),
584 right: Arc::new(right),
585 })
586 }
587
588 fn contains_any(self, left: Expr, right: Expr) -> Expr {
590 Expr::ExprNoExt(ExprNoExt::ContainsAny {
591 left: Arc::new(left),
592 right: Arc::new(right),
593 })
594 }
595
596 fn is_empty(self, expr: Expr) -> Expr {
598 Expr::ExprNoExt(ExprNoExt::IsEmpty {
599 arg: Arc::new(expr),
600 })
601 }
602
603 fn get_tag(self, expr: Expr, tag: Expr) -> Expr {
605 Expr::ExprNoExt(ExprNoExt::GetTag {
606 left: Arc::new(expr),
607 right: Arc::new(tag),
608 })
609 }
610
611 fn has_tag(self, expr: Expr, tag: Expr) -> Expr {
613 Expr::ExprNoExt(ExprNoExt::HasTag {
614 left: Arc::new(expr),
615 right: Arc::new(tag),
616 })
617 }
618
619 fn get_attr(self, expr: Expr, attr: SmolStr) -> Expr {
621 Expr::ExprNoExt(ExprNoExt::GetAttr {
622 left: Arc::new(expr),
623 attr,
624 })
625 }
626
627 fn has_attr(self, expr: Expr, attr: SmolStr) -> Expr {
629 Expr::ExprNoExt(ExprNoExt::HasAttr {
630 left: Arc::new(expr),
631 attr,
632 })
633 }
634
635 fn like(self, expr: Expr, pattern: ast::Pattern) -> Expr {
637 Expr::ExprNoExt(ExprNoExt::Like {
638 left: Arc::new(expr),
639 pattern: pattern.into(),
640 })
641 }
642
643 fn is_entity_type(self, left: Expr, entity_type: ast::EntityType) -> Expr {
645 Expr::ExprNoExt(ExprNoExt::Is {
646 left: Arc::new(left),
647 entity_type: entity_type.to_smolstr(),
648 in_expr: None,
649 })
650 }
651
652 fn is_in_entity_type(self, left: Expr, entity_type: ast::EntityType, entity: Expr) -> Expr {
654 Expr::ExprNoExt(ExprNoExt::Is {
655 left: Arc::new(left),
656 entity_type: entity_type.to_smolstr(),
657 in_expr: Some(Arc::new(entity)),
658 })
659 }
660
661 fn ite(self, cond_expr: Expr, then_expr: Expr, else_expr: Expr) -> Expr {
663 Expr::ExprNoExt(ExprNoExt::If {
664 cond_expr: Arc::new(cond_expr),
665 then_expr: Arc::new(then_expr),
666 else_expr: Arc::new(else_expr),
667 })
668 }
669
670 fn set(self, elements: impl IntoIterator<Item = Expr>) -> Expr {
672 Expr::ExprNoExt(ExprNoExt::Set(elements.into_iter().collect()))
673 }
674
675 fn record(
677 self,
678 map: impl IntoIterator<Item = (SmolStr, Expr)>,
679 ) -> Result<Expr, ast::ExpressionConstructionError> {
680 let mut dedup_map = BTreeMap::new();
681 for (k, v) in map {
682 match dedup_map.entry(k) {
683 btree_map::Entry::Occupied(oentry) => {
684 return Err(ast::expression_construction_errors::DuplicateKeyError {
685 key: oentry.key().clone(),
686 context: "in record literal",
687 }
688 .into());
689 }
690 btree_map::Entry::Vacant(ventry) => {
691 ventry.insert(v);
692 }
693 }
694 }
695 Ok(Expr::ExprNoExt(ExprNoExt::Record(dedup_map)))
696 }
697
698 fn call_extension_fn(self, fn_name: ast::Name, args: impl IntoIterator<Item = Expr>) -> Expr {
700 Expr::ExtFuncCall(ExtFuncCall {
701 call: HashMap::from([(fn_name.to_smolstr(), args.into_iter().collect())]),
702 })
703 }
704
705 #[cfg(feature = "tolerant-ast")]
706 fn error(self, parse_errors: ParseErrors) -> Result<Self::Expr, Self::ErrorType> {
707 Ok(Expr::ExprNoExt(ExprNoExt::Error(
708 AstExprErrorKind::InvalidExpr(parse_errors.to_string()),
709 )))
710 }
711}
712
713impl Expr {
714 pub fn sub_entity_literals(
716 self,
717 mapping: &BTreeMap<EntityUID, EntityUID>,
718 ) -> Result<Self, JsonDeserializationError> {
719 match self.clone() {
720 Expr::ExprNoExt(e) => match e {
721 ExprNoExt::Value(v) => Ok(Expr::ExprNoExt(ExprNoExt::Value(
722 v.sub_entity_literals(mapping)?,
723 ))),
724 ExprNoExt::Var(_) => Ok(self),
725 ExprNoExt::Slot(_) => Ok(self),
726 ExprNoExt::Not { arg } => Ok(Expr::ExprNoExt(ExprNoExt::Not {
727 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
728 })),
729 ExprNoExt::Neg { arg } => Ok(Expr::ExprNoExt(ExprNoExt::Neg {
730 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
731 })),
732 ExprNoExt::Eq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Eq {
733 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
734 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
735 })),
736 ExprNoExt::NotEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::NotEq {
737 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
738 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
739 })),
740 ExprNoExt::In { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::In {
741 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
742 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
743 })),
744 ExprNoExt::Less { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Less {
745 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
746 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
747 })),
748 ExprNoExt::LessEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::LessEq {
749 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
750 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
751 })),
752 ExprNoExt::Greater { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Greater {
753 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
754 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
755 })),
756 ExprNoExt::GreaterEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::GreaterEq {
757 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
758 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
759 })),
760 ExprNoExt::And { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::And {
761 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
762 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
763 })),
764 ExprNoExt::Or { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Or {
765 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
766 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
767 })),
768 ExprNoExt::Add { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Add {
769 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
770 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
771 })),
772 ExprNoExt::Sub { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Sub {
773 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
774 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
775 })),
776 ExprNoExt::Mul { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Mul {
777 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
778 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
779 })),
780 ExprNoExt::Contains { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Contains {
781 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
782 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
783 })),
784 ExprNoExt::ContainsAll { left, right } => {
785 Ok(Expr::ExprNoExt(ExprNoExt::ContainsAll {
786 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
787 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
788 }))
789 }
790 ExprNoExt::ContainsAny { left, right } => {
791 Ok(Expr::ExprNoExt(ExprNoExt::ContainsAny {
792 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
793 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
794 }))
795 }
796 ExprNoExt::IsEmpty { arg } => Ok(Expr::ExprNoExt(ExprNoExt::IsEmpty {
797 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
798 })),
799 ExprNoExt::GetTag { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::GetTag {
800 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
801 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
802 })),
803 ExprNoExt::HasTag { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::HasTag {
804 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
805 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
806 })),
807 ExprNoExt::GetAttr { left, attr } => Ok(Expr::ExprNoExt(ExprNoExt::GetAttr {
808 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
809 attr,
810 })),
811 ExprNoExt::HasAttr { left, attr } => Ok(Expr::ExprNoExt(ExprNoExt::HasAttr {
812 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
813 attr,
814 })),
815 ExprNoExt::Like { left, pattern } => Ok(Expr::ExprNoExt(ExprNoExt::Like {
816 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
817 pattern,
818 })),
819 ExprNoExt::Is {
820 left,
821 entity_type,
822 in_expr,
823 } => match in_expr {
824 Some(in_expr) => Ok(Expr::ExprNoExt(ExprNoExt::Is {
825 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
826 entity_type,
827 in_expr: Some(Arc::new(
828 Arc::unwrap_or_clone(in_expr).sub_entity_literals(mapping)?,
829 )),
830 })),
831 None => Ok(Expr::ExprNoExt(ExprNoExt::Is {
832 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
833 entity_type,
834 in_expr: None,
835 })),
836 },
837 ExprNoExt::If {
838 cond_expr,
839 then_expr,
840 else_expr,
841 } => Ok(Expr::ExprNoExt(ExprNoExt::If {
842 cond_expr: Arc::new(
843 Arc::unwrap_or_clone(cond_expr).sub_entity_literals(mapping)?,
844 ),
845 then_expr: Arc::new(
846 Arc::unwrap_or_clone(then_expr).sub_entity_literals(mapping)?,
847 ),
848 else_expr: Arc::new(
849 Arc::unwrap_or_clone(else_expr).sub_entity_literals(mapping)?,
850 ),
851 })),
852 ExprNoExt::Set(v) => {
853 let mut new_v = vec![];
854 for e in v {
855 new_v.push(e.sub_entity_literals(mapping)?);
856 }
857 Ok(Expr::ExprNoExt(ExprNoExt::Set(new_v)))
858 }
859 ExprNoExt::Record(m) => {
860 let mut new_m = BTreeMap::new();
861 for (k, v) in m {
862 new_m.insert(k, v.sub_entity_literals(mapping)?);
863 }
864 Ok(Expr::ExprNoExt(ExprNoExt::Record(new_m)))
865 }
866 #[cfg(feature = "tolerant-ast")]
867 ExprNoExt::Error(_) => Err(JsonDeserializationError::ASTErrorNode),
868 },
869 Expr::ExtFuncCall(e_fn_call) => {
870 let mut new_m = HashMap::new();
871 for (k, v) in e_fn_call.call {
872 let mut new_v = vec![];
873 for e in v {
874 new_v.push(e.sub_entity_literals(mapping)?);
875 }
876 new_m.insert(k, new_v);
877 }
878 Ok(Expr::ExtFuncCall(ExtFuncCall { call: new_m }))
879 }
880 }
881 }
882}
883
884impl Expr {
885 pub fn try_into_ast(self, id: ast::PolicyID) -> Result<ast::Expr, FromJsonError> {
889 match self {
890 Expr::ExprNoExt(ExprNoExt::Value(jsonvalue)) => jsonvalue
891 .into_expr(|| JsonDeserializationErrorContext::Policy { id: id.clone() })
892 .map(Into::into)
893 .map_err(Into::into),
894 Expr::ExprNoExt(ExprNoExt::Var(var)) => Ok(ast::Expr::var(var)),
895 Expr::ExprNoExt(ExprNoExt::Slot(slot)) => Ok(ast::Expr::slot(slot)),
896 Expr::ExprNoExt(ExprNoExt::Not { arg }) => {
897 Ok(ast::Expr::not(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
898 }
899 Expr::ExprNoExt(ExprNoExt::Neg { arg }) => {
900 Ok(ast::Expr::neg(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
901 }
902 Expr::ExprNoExt(ExprNoExt::Eq { left, right }) => Ok(ast::Expr::is_eq(
903 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
904 Arc::unwrap_or_clone(right).try_into_ast(id)?,
905 )),
906 Expr::ExprNoExt(ExprNoExt::NotEq { left, right }) => Ok(ast::Expr::noteq(
907 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
908 Arc::unwrap_or_clone(right).try_into_ast(id)?,
909 )),
910 Expr::ExprNoExt(ExprNoExt::In { left, right }) => Ok(ast::Expr::is_in(
911 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
912 Arc::unwrap_or_clone(right).try_into_ast(id)?,
913 )),
914 Expr::ExprNoExt(ExprNoExt::Less { left, right }) => Ok(ast::Expr::less(
915 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
916 Arc::unwrap_or_clone(right).try_into_ast(id)?,
917 )),
918 Expr::ExprNoExt(ExprNoExt::LessEq { left, right }) => Ok(ast::Expr::lesseq(
919 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
920 Arc::unwrap_or_clone(right).try_into_ast(id)?,
921 )),
922 Expr::ExprNoExt(ExprNoExt::Greater { left, right }) => Ok(ast::Expr::greater(
923 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
924 Arc::unwrap_or_clone(right).try_into_ast(id)?,
925 )),
926 Expr::ExprNoExt(ExprNoExt::GreaterEq { left, right }) => Ok(ast::Expr::greatereq(
927 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
928 Arc::unwrap_or_clone(right).try_into_ast(id)?,
929 )),
930 Expr::ExprNoExt(ExprNoExt::And { left, right }) => Ok(ast::Expr::and(
931 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
932 Arc::unwrap_or_clone(right).try_into_ast(id)?,
933 )),
934 Expr::ExprNoExt(ExprNoExt::Or { left, right }) => Ok(ast::Expr::or(
935 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
936 Arc::unwrap_or_clone(right).try_into_ast(id)?,
937 )),
938 Expr::ExprNoExt(ExprNoExt::Add { left, right }) => Ok(ast::Expr::add(
939 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
940 Arc::unwrap_or_clone(right).try_into_ast(id)?,
941 )),
942 Expr::ExprNoExt(ExprNoExt::Sub { left, right }) => Ok(ast::Expr::sub(
943 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
944 Arc::unwrap_or_clone(right).try_into_ast(id)?,
945 )),
946 Expr::ExprNoExt(ExprNoExt::Mul { left, right }) => Ok(ast::Expr::mul(
947 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
948 Arc::unwrap_or_clone(right).try_into_ast(id)?,
949 )),
950 Expr::ExprNoExt(ExprNoExt::Contains { left, right }) => Ok(ast::Expr::contains(
951 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
952 Arc::unwrap_or_clone(right).try_into_ast(id)?,
953 )),
954 Expr::ExprNoExt(ExprNoExt::ContainsAll { left, right }) => Ok(ast::Expr::contains_all(
955 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
956 Arc::unwrap_or_clone(right).try_into_ast(id)?,
957 )),
958 Expr::ExprNoExt(ExprNoExt::ContainsAny { left, right }) => Ok(ast::Expr::contains_any(
959 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
960 Arc::unwrap_or_clone(right).try_into_ast(id)?,
961 )),
962 Expr::ExprNoExt(ExprNoExt::IsEmpty { arg }) => Ok(ast::Expr::is_empty(
963 Arc::unwrap_or_clone(arg).try_into_ast(id)?,
964 )),
965 Expr::ExprNoExt(ExprNoExt::GetTag { left, right }) => Ok(ast::Expr::get_tag(
966 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
967 Arc::unwrap_or_clone(right).try_into_ast(id)?,
968 )),
969 Expr::ExprNoExt(ExprNoExt::HasTag { left, right }) => Ok(ast::Expr::has_tag(
970 Arc::unwrap_or_clone(left).try_into_ast(id.clone())?,
971 Arc::unwrap_or_clone(right).try_into_ast(id)?,
972 )),
973 Expr::ExprNoExt(ExprNoExt::GetAttr { left, attr }) => Ok(ast::Expr::get_attr(
974 Arc::unwrap_or_clone(left).try_into_ast(id)?,
975 attr,
976 )),
977 Expr::ExprNoExt(ExprNoExt::HasAttr { left, attr }) => Ok(ast::Expr::has_attr(
978 Arc::unwrap_or_clone(left).try_into_ast(id)?,
979 attr,
980 )),
981 Expr::ExprNoExt(ExprNoExt::Like { left, pattern }) => Ok(ast::Expr::like(
982 Arc::unwrap_or_clone(left).try_into_ast(id)?,
983 crate::ast::Pattern::from(pattern.as_slice()),
984 )),
985 Expr::ExprNoExt(ExprNoExt::Is {
986 left,
987 entity_type,
988 in_expr,
989 }) => ast::EntityType::from_normalized_str(entity_type.as_str())
990 .map_err(FromJsonError::InvalidEntityType)
991 .and_then(|entity_type_name| {
992 let left: ast::Expr = Arc::unwrap_or_clone(left).try_into_ast(id.clone())?;
993 let is_expr = ast::Expr::is_entity_type(left.clone(), entity_type_name);
994 match in_expr {
995 Some(in_expr) => Ok(ast::Expr::and(
998 is_expr,
999 ast::Expr::is_in(left, Arc::unwrap_or_clone(in_expr).try_into_ast(id)?),
1000 )),
1001 None => Ok(is_expr),
1002 }
1003 }),
1004 Expr::ExprNoExt(ExprNoExt::If {
1005 cond_expr,
1006 then_expr,
1007 else_expr,
1008 }) => Ok(ast::Expr::ite(
1009 Arc::unwrap_or_clone(cond_expr).try_into_ast(id.clone())?,
1010 Arc::unwrap_or_clone(then_expr).try_into_ast(id.clone())?,
1011 Arc::unwrap_or_clone(else_expr).try_into_ast(id)?,
1012 )),
1013 Expr::ExprNoExt(ExprNoExt::Set(elements)) => Ok(ast::Expr::set(
1014 elements
1015 .into_iter()
1016 .map(|el| el.try_into_ast(id.clone()))
1017 .collect::<Result<Vec<_>, FromJsonError>>()?,
1018 )),
1019 Expr::ExprNoExt(ExprNoExt::Record(map)) => {
1020 #[allow(clippy::expect_used)]
1022 Ok(ast::Expr::record(
1023 map.into_iter()
1024 .map(|(k, v)| Ok((k, v.try_into_ast(id.clone())?)))
1025 .collect::<Result<HashMap<SmolStr, _>, FromJsonError>>()?,
1026 )
1027 .expect("can't have duplicate keys here because the input was already a HashMap"))
1028 }
1029 Expr::ExtFuncCall(ExtFuncCall { call }) => {
1030 match call.len() {
1031 0 => Err(FromJsonError::MissingOperator),
1032 1 => {
1033 #[allow(clippy::expect_used)]
1035 let (fn_name, args) = call
1036 .into_iter()
1037 .next()
1038 .expect("already checked that len was 1");
1039 let fn_name: ast::Name = fn_name.parse().map_err(|errs| {
1040 JsonDeserializationError::parse_escape(
1041 EscapeKind::Extension,
1042 fn_name,
1043 errs,
1044 )
1045 })?;
1046 if !cst_to_ast::is_known_extension_func_name(&fn_name) {
1047 return Err(FromJsonError::UnknownExtensionFunction(fn_name));
1048 }
1049 Ok(ast::Expr::call_extension_fn(
1050 fn_name,
1051 args.into_iter()
1052 .map(|arg| arg.try_into_ast(id.clone()))
1053 .collect::<Result<_, _>>()?,
1054 ))
1055 }
1056 _ => Err(FromJsonError::MultipleOperators {
1057 ops: call.into_keys().collect(),
1058 }),
1059 }
1060 }
1061 #[cfg(feature = "tolerant-ast")]
1062 Expr::ExprNoExt(ExprNoExt::Error(_)) => Err(FromJsonError::ASTErrorNode),
1063 }
1064 }
1065}
1066
1067#[allow(clippy::fallible_impl_from)]
1069impl<T: Clone> From<ast::Expr<T>> for Expr {
1070 fn from(expr: ast::Expr<T>) -> Expr {
1071 match expr.into_expr_kind() {
1072 ast::ExprKind::Lit(lit) => lit.into(),
1073 ast::ExprKind::Var(var) => var.into(),
1074 ast::ExprKind::Slot(slot) => slot.into(),
1075 ast::ExprKind::Unknown(u) => Builder::new().unknown(u),
1076 ast::ExprKind::If {
1077 test_expr,
1078 then_expr,
1079 else_expr,
1080 } => Builder::new().ite(
1081 Arc::unwrap_or_clone(test_expr).into(),
1082 Arc::unwrap_or_clone(then_expr).into(),
1083 Arc::unwrap_or_clone(else_expr).into(),
1084 ),
1085 ast::ExprKind::And { left, right } => Builder::new().and(
1086 Arc::unwrap_or_clone(left).into(),
1087 Arc::unwrap_or_clone(right).into(),
1088 ),
1089 ast::ExprKind::Or { left, right } => Builder::new().or(
1090 Arc::unwrap_or_clone(left).into(),
1091 Arc::unwrap_or_clone(right).into(),
1092 ),
1093 ast::ExprKind::UnaryApp { op, arg } => {
1094 let arg = Arc::unwrap_or_clone(arg).into();
1095 Builder::new().unary_app(op, arg)
1096 }
1097 ast::ExprKind::BinaryApp { op, arg1, arg2 } => {
1098 let arg1 = Arc::unwrap_or_clone(arg1).into();
1099 let arg2 = Arc::unwrap_or_clone(arg2).into();
1100 Builder::new().binary_app(op, arg1, arg2)
1101 }
1102 ast::ExprKind::ExtensionFunctionApp { fn_name, args } => {
1103 let args = Arc::unwrap_or_clone(args).into_iter().map(Into::into);
1104 Builder::new().call_extension_fn(fn_name, args)
1105 }
1106 ast::ExprKind::GetAttr { expr, attr } => {
1107 Builder::new().get_attr(Arc::unwrap_or_clone(expr).into(), attr)
1108 }
1109 ast::ExprKind::HasAttr { expr, attr } => {
1110 Builder::new().has_attr(Arc::unwrap_or_clone(expr).into(), attr)
1111 }
1112 ast::ExprKind::Like { expr, pattern } => {
1113 Builder::new().like(Arc::unwrap_or_clone(expr).into(), pattern)
1114 }
1115 ast::ExprKind::Is { expr, entity_type } => {
1116 Builder::new().is_entity_type(Arc::unwrap_or_clone(expr).into(), entity_type)
1117 }
1118 ast::ExprKind::Set(set) => {
1119 Builder::new().set(Arc::unwrap_or_clone(set).into_iter().map(Into::into))
1120 }
1121 #[allow(clippy::unwrap_used)]
1123 ast::ExprKind::Record(map) => Builder::new()
1124 .record(
1125 Arc::unwrap_or_clone(map)
1126 .into_iter()
1127 .map(|(k, v)| (k, v.into())),
1128 )
1129 .unwrap(),
1130 #[cfg(feature = "tolerant-ast")]
1131 #[allow(clippy::unwrap_used)]
1133 ast::ExprKind::Error { .. } => Builder::new()
1134 .error(ParseErrors::singleton(ToASTError::new(
1135 ToASTErrorKind::ASTErrorNode,
1136 Loc::new(0..1, "AST_ERROR_NODE".into()),
1137 )))
1138 .unwrap(),
1139 }
1140 }
1141}
1142
1143impl From<ast::Literal> for Expr {
1144 fn from(lit: ast::Literal) -> Expr {
1145 Builder::new().val(lit)
1146 }
1147}
1148
1149impl From<ast::Var> for Expr {
1150 fn from(var: ast::Var) -> Expr {
1151 Builder::new().var(var)
1152 }
1153}
1154
1155impl From<ast::SlotId> for Expr {
1156 fn from(slot: ast::SlotId) -> Expr {
1157 Builder::new().slot(slot)
1158 }
1159}
1160
1161impl TryFrom<&Node<Option<cst::Expr>>> for Expr {
1162 type Error = ParseErrors;
1163 fn try_from(e: &Node<Option<cst::Expr>>) -> Result<Expr, ParseErrors> {
1164 e.to_expr::<Builder>()
1165 }
1166}
1167
1168impl std::fmt::Display for Expr {
1169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1170 match self {
1171 Self::ExprNoExt(e) => write!(f, "{e}"),
1172 Self::ExtFuncCall(e) => write!(f, "{e}"),
1173 }
1174 }
1175}
1176
1177impl BoundedDisplay for Expr {
1178 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1179 match self {
1180 Self::ExprNoExt(e) => BoundedDisplay::fmt(e, f, n),
1181 Self::ExtFuncCall(e) => BoundedDisplay::fmt(e, f, n),
1182 }
1183 }
1184}
1185
1186fn display_cedarvaluejson(
1187 f: &mut impl std::fmt::Write,
1188 v: &CedarValueJson,
1189 n: Option<usize>,
1190) -> std::fmt::Result {
1191 match v {
1192 CedarValueJson::Long(i) if *i < 0 => write!(f, "({i})"),
1195 CedarValueJson::Long(i) => write!(f, "{i}"),
1196 CedarValueJson::Bool(b) => write!(f, "{b}"),
1197 CedarValueJson::String(s) => write!(f, "\"{}\"", s.escape_debug()),
1198 CedarValueJson::EntityEscape { __entity } => {
1199 match ast::EntityUID::try_from(__entity.clone()) {
1200 Ok(euid) => write!(f, "{euid}"),
1201 Err(e) => write!(f, "(invalid entity uid: {})", e),
1202 }
1203 }
1204 CedarValueJson::ExprEscape { __expr } => write!(f, "({__expr})"),
1205 CedarValueJson::ExtnEscape {
1206 __extn: FnAndArg { ext_fn, arg },
1207 } => {
1208 let style = Extensions::all_available().all_funcs().find_map(|f| {
1210 if &f.name().to_string() == ext_fn {
1211 Some(f.style())
1212 } else {
1213 None
1214 }
1215 });
1216 match style {
1217 Some(ast::CallStyle::MethodStyle) => {
1218 display_cedarvaluejson(f, arg, n)?;
1219 write!(f, ".{ext_fn}()")?;
1220 Ok(())
1221 }
1222 Some(ast::CallStyle::FunctionStyle) | None => {
1223 write!(f, "{ext_fn}(")?;
1224 display_cedarvaluejson(f, arg, n)?;
1225 write!(f, ")")?;
1226 Ok(())
1227 }
1228 }
1229 }
1230 CedarValueJson::Set(v) => {
1231 match n {
1232 Some(n) if v.len() > n => {
1233 write!(f, "[")?;
1235 for val in v.iter().take(n) {
1236 display_cedarvaluejson(f, val, Some(n))?;
1237 write!(f, ", ")?;
1238 }
1239 write!(f, "..]")?;
1240 Ok(())
1241 }
1242 _ => {
1243 write!(f, "[")?;
1245 for (i, val) in v.iter().enumerate() {
1246 display_cedarvaluejson(f, val, n)?;
1247 if i < v.len() - 1 {
1248 write!(f, ", ")?;
1249 }
1250 }
1251 write!(f, "]")?;
1252 Ok(())
1253 }
1254 }
1255 }
1256 CedarValueJson::Record(r) => {
1257 match n {
1258 Some(n) if r.len() > n => {
1259 write!(f, "{{")?;
1261 for (k, v) in r.iter().take(n) {
1262 write!(f, "\"{}\": ", k.escape_debug())?;
1263 display_cedarvaluejson(f, v, Some(n))?;
1264 write!(f, ", ")?;
1265 }
1266 write!(f, "..}}")?;
1267 Ok(())
1268 }
1269 _ => {
1270 write!(f, "{{")?;
1272 for (i, (k, v)) in r.iter().enumerate() {
1273 write!(f, "\"{}\": ", k.escape_debug())?;
1274 display_cedarvaluejson(f, v, n)?;
1275 if i < r.len() - 1 {
1276 write!(f, ", ")?;
1277 }
1278 }
1279 write!(f, "}}")?;
1280 Ok(())
1281 }
1282 }
1283 }
1284 CedarValueJson::Null => {
1285 write!(f, "null")?;
1286 Ok(())
1287 }
1288 }
1289}
1290
1291impl std::fmt::Display for ExprNoExt {
1292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1293 BoundedDisplay::fmt_unbounded(self, f)
1294 }
1295}
1296
1297impl BoundedDisplay for ExprNoExt {
1298 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1299 match &self {
1300 ExprNoExt::Value(v) => display_cedarvaluejson(f, v, n),
1301 ExprNoExt::Var(v) => write!(f, "{v}"),
1302 ExprNoExt::Slot(id) => write!(f, "{id}"),
1303 ExprNoExt::Not { arg } => {
1304 write!(f, "!")?;
1305 maybe_with_parens(f, arg, n)
1306 }
1307 ExprNoExt::Neg { arg } => {
1308 write!(f, "-({arg})")
1314 }
1315 ExprNoExt::Eq { left, right } => {
1316 maybe_with_parens(f, left, n)?;
1317 write!(f, " == ")?;
1318 maybe_with_parens(f, right, n)
1319 }
1320 ExprNoExt::NotEq { left, right } => {
1321 maybe_with_parens(f, left, n)?;
1322 write!(f, " != ")?;
1323 maybe_with_parens(f, right, n)
1324 }
1325 ExprNoExt::In { left, right } => {
1326 maybe_with_parens(f, left, n)?;
1327 write!(f, " in ")?;
1328 maybe_with_parens(f, right, n)
1329 }
1330 ExprNoExt::Less { left, right } => {
1331 maybe_with_parens(f, left, n)?;
1332 write!(f, " < ")?;
1333 maybe_with_parens(f, right, n)
1334 }
1335 ExprNoExt::LessEq { left, right } => {
1336 maybe_with_parens(f, left, n)?;
1337 write!(f, " <= ")?;
1338 maybe_with_parens(f, right, n)
1339 }
1340 ExprNoExt::Greater { left, right } => {
1341 maybe_with_parens(f, left, n)?;
1342 write!(f, " > ")?;
1343 maybe_with_parens(f, right, n)
1344 }
1345 ExprNoExt::GreaterEq { left, right } => {
1346 maybe_with_parens(f, left, n)?;
1347 write!(f, " >= ")?;
1348 maybe_with_parens(f, right, n)
1349 }
1350 ExprNoExt::And { left, right } => {
1351 maybe_with_parens(f, left, n)?;
1352 write!(f, " && ")?;
1353 maybe_with_parens(f, right, n)
1354 }
1355 ExprNoExt::Or { left, right } => {
1356 maybe_with_parens(f, left, n)?;
1357 write!(f, " || ")?;
1358 maybe_with_parens(f, right, n)
1359 }
1360 ExprNoExt::Add { left, right } => {
1361 maybe_with_parens(f, left, n)?;
1362 write!(f, " + ")?;
1363 maybe_with_parens(f, right, n)
1364 }
1365 ExprNoExt::Sub { left, right } => {
1366 maybe_with_parens(f, left, n)?;
1367 write!(f, " - ")?;
1368 maybe_with_parens(f, right, n)
1369 }
1370 ExprNoExt::Mul { left, right } => {
1371 maybe_with_parens(f, left, n)?;
1372 write!(f, " * ")?;
1373 maybe_with_parens(f, right, n)
1374 }
1375 ExprNoExt::Contains { left, right } => {
1376 maybe_with_parens(f, left, n)?;
1377 write!(f, ".contains({right})")
1378 }
1379 ExprNoExt::ContainsAll { left, right } => {
1380 maybe_with_parens(f, left, n)?;
1381 write!(f, ".containsAll({right})")
1382 }
1383 ExprNoExt::ContainsAny { left, right } => {
1384 maybe_with_parens(f, left, n)?;
1385 write!(f, ".containsAny({right})")
1386 }
1387 ExprNoExt::IsEmpty { arg } => {
1388 maybe_with_parens(f, arg, n)?;
1389 write!(f, ".isEmpty()")
1390 }
1391 ExprNoExt::GetTag { left, right } => {
1392 maybe_with_parens(f, left, n)?;
1393 write!(f, ".getTag({right})")
1394 }
1395 ExprNoExt::HasTag { left, right } => {
1396 maybe_with_parens(f, left, n)?;
1397 write!(f, ".hasTag({right})")
1398 }
1399 ExprNoExt::GetAttr { left, attr } => {
1400 maybe_with_parens(f, left, n)?;
1401 write!(f, "[\"{}\"]", attr.escape_debug())
1402 }
1403 ExprNoExt::HasAttr { left, attr } => {
1404 maybe_with_parens(f, left, n)?;
1405 write!(f, " has \"{}\"", attr.escape_debug())
1406 }
1407 ExprNoExt::Like { left, pattern } => {
1408 maybe_with_parens(f, left, n)?;
1409 write!(
1410 f,
1411 " like \"{}\"",
1412 crate::ast::Pattern::from(pattern.as_slice())
1413 )
1414 }
1415 ExprNoExt::Is {
1416 left,
1417 entity_type,
1418 in_expr,
1419 } => {
1420 maybe_with_parens(f, left, n)?;
1421 write!(f, " is {entity_type}")?;
1422 match in_expr {
1423 Some(in_expr) => {
1424 write!(f, " in ")?;
1425 maybe_with_parens(f, in_expr, n)
1426 }
1427 None => Ok(()),
1428 }
1429 }
1430 ExprNoExt::If {
1431 cond_expr,
1432 then_expr,
1433 else_expr,
1434 } => {
1435 write!(f, "if ")?;
1436 maybe_with_parens(f, cond_expr, n)?;
1437 write!(f, " then ")?;
1438 maybe_with_parens(f, then_expr, n)?;
1439 write!(f, " else ")?;
1440 maybe_with_parens(f, else_expr, n)
1441 }
1442 ExprNoExt::Set(v) => {
1443 match n {
1444 Some(n) if v.len() > n => {
1445 write!(f, "[")?;
1447 for element in v.iter().take(n) {
1448 BoundedDisplay::fmt(element, f, Some(n))?;
1449 write!(f, ", ")?;
1450 }
1451 write!(f, "..]")?;
1452 Ok(())
1453 }
1454 _ => {
1455 write!(f, "[")?;
1457 for (i, element) in v.iter().enumerate() {
1458 BoundedDisplay::fmt(element, f, n)?;
1459 if i < v.len() - 1 {
1460 write!(f, ", ")?;
1461 }
1462 }
1463 write!(f, "]")?;
1464 Ok(())
1465 }
1466 }
1467 }
1468 ExprNoExt::Record(m) => {
1469 match n {
1470 Some(n) if m.len() > n => {
1471 write!(f, "{{")?;
1473 for (k, v) in m.iter().take(n) {
1474 write!(f, "\"{}\": ", k.escape_debug())?;
1475 BoundedDisplay::fmt(v, f, Some(n))?;
1476 write!(f, ", ")?;
1477 }
1478 write!(f, "..}}")?;
1479 Ok(())
1480 }
1481 _ => {
1482 write!(f, "{{")?;
1484 for (i, (k, v)) in m.iter().enumerate() {
1485 write!(f, "\"{}\": ", k.escape_debug())?;
1486 BoundedDisplay::fmt(v, f, n)?;
1487 if i < m.len() - 1 {
1488 write!(f, ", ")?;
1489 }
1490 }
1491 write!(f, "}}")?;
1492 Ok(())
1493 }
1494 }
1495 }
1496 #[cfg(feature = "tolerant-ast")]
1497 ExprNoExt::Error(e) => {
1498 write!(f, "{e}")?;
1499 Ok(())
1500 }
1501 }
1502 }
1503}
1504
1505impl std::fmt::Display for ExtFuncCall {
1506 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1507 BoundedDisplay::fmt_unbounded(self, f)
1508 }
1509}
1510
1511impl BoundedDisplay for ExtFuncCall {
1512 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1513 #[allow(clippy::unreachable)]
1515 let Some((fn_name, args)) = self.call.iter().next() else {
1516 unreachable!("invariant violated: empty ExtFuncCall")
1517 };
1518 let style = Extensions::all_available().all_funcs().find_map(|ext_fn| {
1520 if &ext_fn.name().to_string() == fn_name {
1521 Some(ext_fn.style())
1522 } else {
1523 None
1524 }
1525 });
1526 match (style, args.iter().next()) {
1527 (Some(ast::CallStyle::MethodStyle), Some(receiver)) => {
1528 maybe_with_parens(f, receiver, n)?;
1529 write!(f, ".{}({})", fn_name, args.iter().skip(1).join(", "))
1530 }
1531 (_, _) => {
1532 write!(f, "{}({})", fn_name, args.iter().join(", "))
1533 }
1534 }
1535 }
1536}
1537
1538fn maybe_with_parens(
1544 f: &mut impl std::fmt::Write,
1545 expr: &Expr,
1546 n: Option<usize>,
1547) -> std::fmt::Result {
1548 match expr {
1549 Expr::ExprNoExt(ExprNoExt::Set(_)) |
1550 Expr::ExprNoExt(ExprNoExt::Record(_)) |
1551 Expr::ExprNoExt(ExprNoExt::Value(_)) |
1552 Expr::ExprNoExt(ExprNoExt::Var(_)) |
1553 Expr::ExprNoExt(ExprNoExt::Slot(_)) => BoundedDisplay::fmt(expr, f, n),
1554
1555 Expr::ExprNoExt(ExprNoExt::Not { .. }) |
1558 Expr::ExprNoExt(ExprNoExt::Neg { .. }) |
1561 Expr::ExprNoExt(ExprNoExt::Eq { .. }) |
1562 Expr::ExprNoExt(ExprNoExt::NotEq { .. }) |
1563 Expr::ExprNoExt(ExprNoExt::In { .. }) |
1564 Expr::ExprNoExt(ExprNoExt::Less { .. }) |
1565 Expr::ExprNoExt(ExprNoExt::LessEq { .. }) |
1566 Expr::ExprNoExt(ExprNoExt::Greater { .. }) |
1567 Expr::ExprNoExt(ExprNoExt::GreaterEq { .. }) |
1568 Expr::ExprNoExt(ExprNoExt::And { .. }) |
1569 Expr::ExprNoExt(ExprNoExt::Or { .. }) |
1570 Expr::ExprNoExt(ExprNoExt::Add { .. }) |
1571 Expr::ExprNoExt(ExprNoExt::Sub { .. }) |
1572 Expr::ExprNoExt(ExprNoExt::Mul { .. }) |
1573 Expr::ExprNoExt(ExprNoExt::Contains { .. }) |
1574 Expr::ExprNoExt(ExprNoExt::ContainsAll { .. }) |
1575 Expr::ExprNoExt(ExprNoExt::ContainsAny { .. }) |
1576 Expr::ExprNoExt(ExprNoExt::IsEmpty { .. }) |
1577 Expr::ExprNoExt(ExprNoExt::GetAttr { .. }) |
1578 Expr::ExprNoExt(ExprNoExt::HasAttr { .. }) |
1579 Expr::ExprNoExt(ExprNoExt::GetTag { .. }) |
1580 Expr::ExprNoExt(ExprNoExt::HasTag { .. }) |
1581 Expr::ExprNoExt(ExprNoExt::Like { .. }) |
1582 Expr::ExprNoExt(ExprNoExt::Is { .. }) |
1583 Expr::ExprNoExt(ExprNoExt::If { .. }) |
1584 Expr::ExtFuncCall { .. } => {
1585 write!(f, "(")?;
1586 BoundedDisplay::fmt(expr, f, n)?;
1587 write!(f, ")")?;
1588 Ok(())
1589 },
1590 #[cfg(feature = "tolerant-ast")]
1591 Expr::ExprNoExt(ExprNoExt::Error { .. }) => {
1592 write!(f, "(")?;
1593 BoundedDisplay::fmt(expr, f, n)?;
1594 write!(f, ")")?;
1595 Ok(())
1596 }
1597 }
1598}
1599
1600#[cfg(test)]
1601#[allow(clippy::indexing_slicing)]
1603#[allow(clippy::panic)]
1605mod test {
1606 use crate::parser::{
1607 err::{ParseError, ToASTErrorKind},
1608 parse_expr,
1609 };
1610
1611 use super::*;
1612 use ast::BoundedToString;
1613 use cool_asserts::assert_matches;
1614
1615 #[test]
1616 fn test_invalid_expr_from_cst_name() {
1617 let e = crate::parser::text_to_cst::parse_expr("some_long_str::else").unwrap();
1618 assert_matches!(Expr::try_from(&e), Err(e) => {
1619 assert!(e.len() == 1);
1620 assert_matches!(&e[0],
1621 ParseError::ToAST(to_ast_error) => {
1622 assert_matches!(to_ast_error.kind(), ToASTErrorKind::ReservedIdentifier(s) => {
1623 assert_eq!(s.to_string(), "else");
1624 });
1625 }
1626 );
1627 });
1628 }
1629
1630 #[test]
1631 fn display_and_bounded_display() {
1632 let expr = Expr::from(parse_expr(r#"[100, [3, 4, 5], -20, "foo"]"#).unwrap());
1633 assert_eq!(format!("{expr}"), r#"[100, [3, 4, 5], (-20), "foo"]"#);
1634 assert_eq!(
1635 BoundedToString::to_string(&expr, None),
1636 r#"[100, [3, 4, 5], (-20), "foo"]"#
1637 );
1638 assert_eq!(
1639 BoundedToString::to_string(&expr, Some(4)),
1640 r#"[100, [3, 4, 5], (-20), "foo"]"#
1641 );
1642 assert_eq!(
1643 BoundedToString::to_string(&expr, Some(3)),
1644 r#"[100, [3, 4, 5], (-20), ..]"#
1645 );
1646 assert_eq!(
1647 BoundedToString::to_string(&expr, Some(2)),
1648 r#"[100, [3, 4, ..], ..]"#
1649 );
1650 assert_eq!(BoundedToString::to_string(&expr, Some(1)), r#"[100, ..]"#);
1651 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"[..]"#);
1652
1653 let expr = Expr::from(
1654 parse_expr(
1655 r#"{
1656 a: 12,
1657 b: [3, 4, true],
1658 c: -20,
1659 "hello ∞ world": "∂µß≈¥"
1660 }"#,
1661 )
1662 .unwrap(),
1663 );
1664 assert_eq!(
1665 format!("{expr}"),
1666 r#"{"a": 12, "b": [3, 4, true], "c": (-20), "hello ∞ world": "∂µß≈¥"}"#
1667 );
1668 assert_eq!(
1669 BoundedToString::to_string(&expr, None),
1670 r#"{"a": 12, "b": [3, 4, true], "c": (-20), "hello ∞ world": "∂µß≈¥"}"#
1671 );
1672 assert_eq!(
1673 BoundedToString::to_string(&expr, Some(4)),
1674 r#"{"a": 12, "b": [3, 4, true], "c": (-20), "hello ∞ world": "∂µß≈¥"}"#
1675 );
1676 assert_eq!(
1677 BoundedToString::to_string(&expr, Some(3)),
1678 r#"{"a": 12, "b": [3, 4, true], "c": (-20), ..}"#
1679 );
1680 assert_eq!(
1681 BoundedToString::to_string(&expr, Some(2)),
1682 r#"{"a": 12, "b": [3, 4, ..], ..}"#
1683 );
1684 assert_eq!(
1685 BoundedToString::to_string(&expr, Some(1)),
1686 r#"{"a": 12, ..}"#
1687 );
1688 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"{..}"#);
1689 }
1690}