1use super::FromJsonError;
18#[cfg(feature = "tolerant-ast")]
19use crate::ast::expr_allows_errors::AstExprErrorKind;
20use crate::ast::Infallible;
21use crate::ast::{self, is_normalized_ident, BoundedDisplay, EntityUID, Name};
22use crate::entities::json::{
23 err::EscapeKind, err::JsonDeserializationError, err::JsonDeserializationErrorContext,
24 CedarValueJson, FnAndArgs,
25};
26use crate::expr_builder::{ExprBuilder, ExprBuilderInfallibleBuild};
27use crate::extensions::{ExtStyles, Extensions};
28use crate::jsonvalue::JsonValueWithNoDuplicateKeys;
29use crate::parser::{cst, err::ParseErrors, Loc, Node};
30use crate::FromNormalizedStr;
31use itertools::Itertools;
32use nonempty::NonEmpty;
33use serde::{de::Visitor, Deserialize, Serialize};
34use serde_with::serde_as;
35use smol_str::{SmolStr, ToSmolStr};
36use std::collections::{btree_map, BTreeMap, HashMap};
37use std::sync::Arc;
38
39#[derive(Debug, Clone, PartialEq, Serialize)]
41#[serde(untagged)]
42#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
43#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
44pub enum Expr {
45 ExprNoExt(ExprNoExt),
47 ExtFuncCall(ExtFuncCall),
50}
51
52impl<'de> Deserialize<'de> for Expr {
62 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63 where
64 D: serde::Deserializer<'de>,
65 {
66 struct ExprVisitor;
67 impl<'de> Visitor<'de> for ExprVisitor {
68 type Value = Expr;
69 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 formatter.write_str("JSON object representing an expression")
71 }
72
73 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
74 where
75 A: serde::de::MapAccess<'de>,
76 {
77 let (k, v): (SmolStr, JsonValueWithNoDuplicateKeys) = match map.next_entry()? {
78 None => {
79 return Err(serde::de::Error::custom(
80 "empty map is not a valid expression",
81 ))
82 }
83 Some((k, v)) => (k, v),
84 };
85 match map.next_key()? {
86 None => (),
87 Some(k2) => {
88 let k2: SmolStr = k2;
89 return Err(serde::de::Error::custom(format!("JSON object representing an `Expr` should have only one key, but found two keys: `{k}` and `{k2}`")));
90 }
91 };
92 if ExtStyles::is_known_extension_func_str(&k) {
93 let obj = serde_json::json!({ k: v });
97 let extfunccall =
98 serde_json::from_value(obj).map_err(serde::de::Error::custom)?;
99 Ok(Expr::ExtFuncCall(extfunccall))
100 } else {
101 let obj = serde_json::json!({ k: v });
104 let exprnoext =
105 serde_json::from_value(obj).map_err(serde::de::Error::custom)?;
106 Ok(Expr::ExprNoExt(exprnoext))
107 }
108 }
109 }
110
111 deserializer.deserialize_map(ExprVisitor)
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
118#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
119pub enum PatternElem {
120 Wildcard,
122 Literal(SmolStr),
124}
125
126impl From<&[PatternElem]> for crate::ast::Pattern {
127 fn from(value: &[PatternElem]) -> Self {
128 let mut elems = Vec::new();
129 for elem in value {
130 match elem {
131 PatternElem::Wildcard => {
132 elems.push(crate::ast::PatternElem::Wildcard);
133 }
134 PatternElem::Literal(s) => {
135 elems.extend(s.chars().map(crate::ast::PatternElem::Char));
136 }
137 }
138 }
139 Self::from(elems)
140 }
141}
142
143impl From<crate::ast::PatternElem> for PatternElem {
144 fn from(value: crate::ast::PatternElem) -> Self {
145 match value {
146 crate::ast::PatternElem::Wildcard => Self::Wildcard,
147 crate::ast::PatternElem::Char(c) => Self::Literal(c.to_smolstr()),
148 }
149 }
150}
151
152impl From<crate::ast::Pattern> for Vec<PatternElem> {
153 fn from(value: crate::ast::Pattern) -> Self {
154 value.iter().map(|elem| (*elem).into()).collect()
155 }
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161#[serde(untagged)]
162#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
163#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi,))]
164pub enum HasAttrRepr {
165 Simple {
168 left: Arc<Expr>,
170 attr: SmolStr,
172 },
173 Extended {
176 left: Arc<Expr>,
178 attr: NonEmpty<SmolStr>,
180 },
181}
182
183#[serde_as]
186#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
187#[serde(deny_unknown_fields)]
188#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
189#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
190pub enum ExprNoExt {
191 Value(CedarValueJson),
194 Var(ast::Var),
196 Slot(#[cfg_attr(feature = "wasm", tsify(type = "string"))] ast::SlotId),
198 #[serde(rename = "!")]
200 Not {
201 arg: Arc<Expr>,
203 },
204 #[serde(rename = "neg")]
206 Neg {
207 arg: Arc<Expr>,
209 },
210 #[serde(rename = "==")]
212 Eq {
213 left: Arc<Expr>,
215 right: Arc<Expr>,
217 },
218 #[serde(rename = "!=")]
220 NotEq {
221 left: Arc<Expr>,
223 right: Arc<Expr>,
225 },
226 #[serde(rename = "in")]
228 In {
229 left: Arc<Expr>,
231 right: Arc<Expr>,
233 },
234 #[serde(rename = "<")]
236 Less {
237 left: Arc<Expr>,
239 right: Arc<Expr>,
241 },
242 #[serde(rename = "<=")]
244 LessEq {
245 left: Arc<Expr>,
247 right: Arc<Expr>,
249 },
250 #[serde(rename = ">")]
252 Greater {
253 left: Arc<Expr>,
255 right: Arc<Expr>,
257 },
258 #[serde(rename = ">=")]
260 GreaterEq {
261 left: Arc<Expr>,
263 right: Arc<Expr>,
265 },
266 #[serde(rename = "&&")]
268 And {
269 left: Arc<Expr>,
271 right: Arc<Expr>,
273 },
274 #[serde(rename = "||")]
276 Or {
277 left: Arc<Expr>,
279 right: Arc<Expr>,
281 },
282 #[serde(rename = "+")]
284 Add {
285 left: Arc<Expr>,
287 right: Arc<Expr>,
289 },
290 #[serde(rename = "-")]
292 Sub {
293 left: Arc<Expr>,
295 right: Arc<Expr>,
297 },
298 #[serde(rename = "*")]
300 Mul {
301 left: Arc<Expr>,
303 right: Arc<Expr>,
305 },
306 #[serde(rename = "contains")]
308 Contains {
309 left: Arc<Expr>,
311 right: Arc<Expr>,
313 },
314 #[serde(rename = "containsAll")]
316 ContainsAll {
317 left: Arc<Expr>,
319 right: Arc<Expr>,
321 },
322 #[serde(rename = "containsAny")]
324 ContainsAny {
325 left: Arc<Expr>,
327 right: Arc<Expr>,
329 },
330 #[serde(rename = "isEmpty")]
332 IsEmpty {
333 arg: Arc<Expr>,
335 },
336 #[serde(rename = "getTag")]
338 GetTag {
339 left: Arc<Expr>,
341 right: Arc<Expr>,
343 },
344 #[serde(rename = "hasTag")]
346 HasTag {
347 left: Arc<Expr>,
349 right: Arc<Expr>,
351 },
352 #[serde(rename = ".")]
354 GetAttr {
355 left: Arc<Expr>,
357 attr: SmolStr,
359 },
360 #[serde(rename = "has")]
362 HasAttr(HasAttrRepr),
363 #[serde(rename = "like")]
365 Like {
366 left: Arc<Expr>,
368 pattern: Vec<PatternElem>,
370 },
371 #[serde(rename = "is")]
373 Is {
374 left: Arc<Expr>,
376 entity_type: SmolStr,
378 #[serde(skip_serializing_if = "Option::is_none")]
380 #[serde(rename = "in")]
381 in_expr: Option<Arc<Expr>>,
382 },
383 #[serde(rename = "if-then-else")]
385 If {
386 #[serde(rename = "if")]
388 cond_expr: Arc<Expr>,
389 #[serde(rename = "then")]
391 then_expr: Arc<Expr>,
392 #[serde(rename = "else")]
394 else_expr: Arc<Expr>,
395 },
396 Set(Vec<Expr>),
400 Record(
404 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
405 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, Expr>"))]
406 BTreeMap<SmolStr, Expr>,
407 ),
408 #[cfg(feature = "tolerant-ast")]
410 Error(AstExprErrorKind),
411}
412
413#[serde_as]
415#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
416#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
417#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
418pub struct ExtFuncCall {
419 #[serde(flatten)]
428 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
429 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, Array<Expr>>"))]
430 call: HashMap<SmolStr, Vec<Expr>>,
431}
432
433impl ExtFuncCall {
434 fn invariant(&self) -> Result<(), FromJsonError> {
438 match self.call.len() {
439 0 => Err(FromJsonError::MissingOperator),
440 1 => Ok(()),
441 _ => Err(FromJsonError::MultipleOperators {
442 ops: self.call.clone().into_keys().collect(),
443 }),
444 }
445 }
446
447 pub(crate) fn try_into_components(self) -> Result<(SmolStr, Vec<Expr>), FromJsonError> {
450 self.invariant()?;
451 match self.call.into_iter().next() {
452 Some((fn_name, args)) => Ok((fn_name, args)),
453 None => Err(FromJsonError::MissingOperator),
454 }
455 }
456
457 pub(crate) fn try_components(&self) -> Result<(&SmolStr, &[Expr]), FromJsonError> {
460 self.invariant()?;
461 match self.call.iter().next() {
462 Some((fn_name, args)) => Ok((fn_name, args)),
463 None => Err(FromJsonError::MissingOperator),
464 }
465 }
466}
467
468#[derive(Clone, Debug)]
470pub struct Builder;
471
472impl ExprBuilderInfallibleBuild for Builder {}
473
474impl ExprBuilder for Builder {
475 type Expr = Expr;
476 type BuildError = Infallible;
477 type Data = ();
478 #[cfg(feature = "tolerant-ast")]
479 type ErrorType = Infallible;
480
481 fn with_data(_data: Self::Data) -> Self {
482 Self
483 }
484
485 fn with_maybe_source_loc(self, _: Option<&Loc>) -> Self {
486 self
487 }
488
489 fn loc(&self) -> Option<&Loc> {
490 None
491 }
492
493 fn data(&self) -> &Self::Data {
494 &()
495 }
496
497 fn val(self, lit: impl Into<ast::Literal>) -> Expr {
499 Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::from_lit(lit.into())))
500 }
501
502 fn var(self, var: ast::Var) -> Expr {
504 Expr::ExprNoExt(ExprNoExt::Var(var))
505 }
506
507 fn slot(self, slot: ast::SlotId) -> Expr {
509 Expr::ExprNoExt(ExprNoExt::Slot(slot))
510 }
511
512 fn unknown(self, u: ast::Unknown) -> Expr {
514 Expr::ExtFuncCall(ExtFuncCall {
515 call: HashMap::from([("unknown".to_smolstr(), vec![Builder::new().val(u.name)])]),
516 })
517 }
518
519 fn not(self, e: Expr) -> Expr {
521 Expr::ExprNoExt(ExprNoExt::Not { arg: Arc::new(e) })
522 }
523
524 fn neg(self, e: Expr) -> Expr {
526 Expr::ExprNoExt(ExprNoExt::Neg { arg: Arc::new(e) })
527 }
528
529 fn is_eq(self, left: Expr, right: Expr) -> Expr {
531 Expr::ExprNoExt(ExprNoExt::Eq {
532 left: Arc::new(left),
533 right: Arc::new(right),
534 })
535 }
536
537 fn noteq(self, left: Expr, right: Expr) -> Expr {
539 Expr::ExprNoExt(ExprNoExt::NotEq {
540 left: Arc::new(left),
541 right: Arc::new(right),
542 })
543 }
544
545 fn is_in_arc(self, left: Arc<Expr>, right: Arc<Expr>) -> Expr {
547 Expr::ExprNoExt(ExprNoExt::In { left, right })
548 }
549
550 fn less(self, left: Expr, right: Expr) -> Expr {
552 Expr::ExprNoExt(ExprNoExt::Less {
553 left: Arc::new(left),
554 right: Arc::new(right),
555 })
556 }
557
558 fn lesseq(self, left: Expr, right: Expr) -> Expr {
560 Expr::ExprNoExt(ExprNoExt::LessEq {
561 left: Arc::new(left),
562 right: Arc::new(right),
563 })
564 }
565
566 fn greater(self, left: Expr, right: Expr) -> Expr {
568 Expr::ExprNoExt(ExprNoExt::Greater {
569 left: Arc::new(left),
570 right: Arc::new(right),
571 })
572 }
573
574 fn greatereq(self, left: Expr, right: Expr) -> Expr {
576 Expr::ExprNoExt(ExprNoExt::GreaterEq {
577 left: Arc::new(left),
578 right: Arc::new(right),
579 })
580 }
581
582 fn and(self, left: Expr, right: Expr) -> Expr {
584 Expr::ExprNoExt(ExprNoExt::And {
585 left: Arc::new(left),
586 right: Arc::new(right),
587 })
588 }
589
590 fn or(self, left: Expr, right: Expr) -> Expr {
592 Expr::ExprNoExt(ExprNoExt::Or {
593 left: Arc::new(left),
594 right: Arc::new(right),
595 })
596 }
597
598 fn add(self, left: Expr, right: Expr) -> Expr {
600 Expr::ExprNoExt(ExprNoExt::Add {
601 left: Arc::new(left),
602 right: Arc::new(right),
603 })
604 }
605
606 fn sub(self, left: Expr, right: Expr) -> Expr {
608 Expr::ExprNoExt(ExprNoExt::Sub {
609 left: Arc::new(left),
610 right: Arc::new(right),
611 })
612 }
613
614 fn mul(self, left: Expr, right: Expr) -> Expr {
616 Expr::ExprNoExt(ExprNoExt::Mul {
617 left: Arc::new(left),
618 right: Arc::new(right),
619 })
620 }
621
622 fn contains(self, left: Expr, right: Expr) -> Expr {
624 Expr::ExprNoExt(ExprNoExt::Contains {
625 left: Arc::new(left),
626 right: Arc::new(right),
627 })
628 }
629
630 fn contains_all(self, left: Expr, right: Expr) -> Expr {
632 Expr::ExprNoExt(ExprNoExt::ContainsAll {
633 left: Arc::new(left),
634 right: Arc::new(right),
635 })
636 }
637
638 fn contains_any(self, left: Expr, right: Expr) -> Expr {
640 Expr::ExprNoExt(ExprNoExt::ContainsAny {
641 left: Arc::new(left),
642 right: Arc::new(right),
643 })
644 }
645
646 fn is_empty(self, expr: Expr) -> Expr {
648 Expr::ExprNoExt(ExprNoExt::IsEmpty {
649 arg: Arc::new(expr),
650 })
651 }
652
653 fn get_tag(self, expr: Expr, tag: Expr) -> Expr {
655 Expr::ExprNoExt(ExprNoExt::GetTag {
656 left: Arc::new(expr),
657 right: Arc::new(tag),
658 })
659 }
660
661 fn has_tag(self, expr: Expr, tag: Expr) -> Expr {
663 Expr::ExprNoExt(ExprNoExt::HasTag {
664 left: Arc::new(expr),
665 right: Arc::new(tag),
666 })
667 }
668
669 fn get_attr(self, expr: Expr, attr: SmolStr) -> Expr {
671 Expr::ExprNoExt(ExprNoExt::GetAttr {
672 left: Arc::new(expr),
673 attr,
674 })
675 }
676
677 fn get_attr_arc(self, expr: Arc<Expr>, attr: SmolStr) -> Expr {
679 Expr::ExprNoExt(ExprNoExt::GetAttr { left: expr, attr })
680 }
681
682 fn has_attr(self, expr: Expr, attr: SmolStr) -> Expr {
684 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
685 left: Arc::new(expr),
686 attr,
687 }))
688 }
689
690 fn has_attr_arc(self, expr: Arc<Self::Expr>, attr: SmolStr) -> Self::Expr {
692 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple { left: expr, attr }))
693 }
694
695 fn like(self, expr: Expr, pattern: ast::Pattern) -> Expr {
697 Expr::ExprNoExt(ExprNoExt::Like {
698 left: Arc::new(expr),
699 pattern: pattern.into(),
700 })
701 }
702
703 fn is_entity_type_arc(self, left: Arc<Expr>, entity_type: ast::EntityType) -> Expr {
705 Expr::ExprNoExt(ExprNoExt::Is {
706 left,
707 entity_type: entity_type.to_smolstr(),
708 in_expr: None,
709 })
710 }
711
712 fn is_in_entity_type(self, left: Expr, entity_type: ast::EntityType, entity: Expr) -> Expr {
714 Expr::ExprNoExt(ExprNoExt::Is {
715 left: Arc::new(left),
716 entity_type: entity_type.to_smolstr(),
717 in_expr: Some(Arc::new(entity)),
718 })
719 }
720
721 fn ite_arc(
723 self,
724 cond_expr: Arc<Self::Expr>,
725 then_expr: Arc<Self::Expr>,
726 else_expr: Arc<Self::Expr>,
727 ) -> Self::Expr {
728 Expr::ExprNoExt(ExprNoExt::If {
729 cond_expr,
730 then_expr,
731 else_expr,
732 })
733 }
734
735 fn set(self, elements: impl IntoIterator<Item = Expr>) -> Expr {
737 Expr::ExprNoExt(ExprNoExt::Set(elements.into_iter().collect()))
738 }
739
740 fn record(
742 self,
743 map: impl IntoIterator<Item = (SmolStr, Expr)>,
744 ) -> Result<Expr, ast::ExpressionConstructionError> {
745 let mut dedup_map = BTreeMap::new();
746 for (k, v) in map {
747 match dedup_map.entry(k) {
748 btree_map::Entry::Occupied(oentry) => {
749 return Err(ast::expression_construction_errors::DuplicateKeyError {
750 key: oentry.key().clone(),
751 context: "in record literal",
752 }
753 .into());
754 }
755 btree_map::Entry::Vacant(ventry) => {
756 ventry.insert(v);
757 }
758 }
759 }
760 Ok(Expr::ExprNoExt(ExprNoExt::Record(dedup_map)))
761 }
762
763 fn call_extension_fn(
765 self,
766 fn_name: ast::Name,
767 args: impl IntoIterator<Item = Expr>,
768 ) -> Result<Expr, Infallible> {
769 Ok(Expr::ExtFuncCall(ExtFuncCall {
770 call: HashMap::from([(fn_name.to_smolstr(), args.into_iter().collect())]),
771 }))
772 }
773
774 #[cfg(feature = "tolerant-ast")]
775 fn error(self, parse_errors: ParseErrors) -> Result<Self::Expr, Self::ErrorType> {
776 Ok(Expr::ExprNoExt(ExprNoExt::Error(
777 AstExprErrorKind::InvalidExpr(parse_errors.to_string()),
778 )))
779 }
780}
781
782impl Expr {
783 pub fn sub_entity_literals(
785 self,
786 mapping: &BTreeMap<EntityUID, EntityUID>,
787 ) -> Result<Self, JsonDeserializationError> {
788 match self {
789 Expr::ExprNoExt(e) => match e {
790 ExprNoExt::Value(v) => Ok(Expr::ExprNoExt(ExprNoExt::Value(
791 v.sub_entity_literals(mapping)?,
792 ))),
793 v @ ExprNoExt::Var(_) => Ok(Expr::ExprNoExt(v)),
794 s @ ExprNoExt::Slot(_) => Ok(Expr::ExprNoExt(s)),
795 ExprNoExt::Not { arg } => Ok(Expr::ExprNoExt(ExprNoExt::Not {
796 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
797 })),
798 ExprNoExt::Neg { arg } => Ok(Expr::ExprNoExt(ExprNoExt::Neg {
799 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
800 })),
801 ExprNoExt::Eq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Eq {
802 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
803 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
804 })),
805 ExprNoExt::NotEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::NotEq {
806 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
807 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
808 })),
809 ExprNoExt::In { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::In {
810 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
811 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
812 })),
813 ExprNoExt::Less { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Less {
814 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
815 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
816 })),
817 ExprNoExt::LessEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::LessEq {
818 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
819 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
820 })),
821 ExprNoExt::Greater { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Greater {
822 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
823 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
824 })),
825 ExprNoExt::GreaterEq { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::GreaterEq {
826 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
827 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
828 })),
829 ExprNoExt::And { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::And {
830 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
831 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
832 })),
833 ExprNoExt::Or { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Or {
834 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
835 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
836 })),
837 ExprNoExt::Add { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Add {
838 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
839 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
840 })),
841 ExprNoExt::Sub { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Sub {
842 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
843 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
844 })),
845 ExprNoExt::Mul { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Mul {
846 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
847 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
848 })),
849 ExprNoExt::Contains { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::Contains {
850 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
851 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
852 })),
853 ExprNoExt::ContainsAll { left, right } => {
854 Ok(Expr::ExprNoExt(ExprNoExt::ContainsAll {
855 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
856 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
857 }))
858 }
859 ExprNoExt::ContainsAny { left, right } => {
860 Ok(Expr::ExprNoExt(ExprNoExt::ContainsAny {
861 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
862 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
863 }))
864 }
865 ExprNoExt::IsEmpty { arg } => Ok(Expr::ExprNoExt(ExprNoExt::IsEmpty {
866 arg: Arc::new(Arc::unwrap_or_clone(arg).sub_entity_literals(mapping)?),
867 })),
868 ExprNoExt::GetTag { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::GetTag {
869 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
870 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
871 })),
872 ExprNoExt::HasTag { left, right } => Ok(Expr::ExprNoExt(ExprNoExt::HasTag {
873 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
874 right: Arc::new(Arc::unwrap_or_clone(right).sub_entity_literals(mapping)?),
875 })),
876 ExprNoExt::GetAttr { left, attr } => Ok(Expr::ExprNoExt(ExprNoExt::GetAttr {
877 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
878 attr,
879 })),
880 ExprNoExt::HasAttr(repr) => match repr {
881 HasAttrRepr::Simple { left, attr } => {
882 Ok(Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
883 left: Arc::new(
884 Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?,
885 ),
886 attr,
887 })))
888 }
889 HasAttrRepr::Extended { left, attr } => {
890 Ok(Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
891 left: Arc::new(
892 Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?,
893 ),
894 attr,
895 })))
896 }
897 },
898 ExprNoExt::Like { left, pattern } => Ok(Expr::ExprNoExt(ExprNoExt::Like {
899 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
900 pattern,
901 })),
902 ExprNoExt::Is {
903 left,
904 entity_type,
905 in_expr,
906 } => match in_expr {
907 Some(in_expr) => Ok(Expr::ExprNoExt(ExprNoExt::Is {
908 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
909 entity_type,
910 in_expr: Some(Arc::new(
911 Arc::unwrap_or_clone(in_expr).sub_entity_literals(mapping)?,
912 )),
913 })),
914 None => Ok(Expr::ExprNoExt(ExprNoExt::Is {
915 left: Arc::new(Arc::unwrap_or_clone(left).sub_entity_literals(mapping)?),
916 entity_type,
917 in_expr: None,
918 })),
919 },
920 ExprNoExt::If {
921 cond_expr,
922 then_expr,
923 else_expr,
924 } => Ok(Expr::ExprNoExt(ExprNoExt::If {
925 cond_expr: Arc::new(
926 Arc::unwrap_or_clone(cond_expr).sub_entity_literals(mapping)?,
927 ),
928 then_expr: Arc::new(
929 Arc::unwrap_or_clone(then_expr).sub_entity_literals(mapping)?,
930 ),
931 else_expr: Arc::new(
932 Arc::unwrap_or_clone(else_expr).sub_entity_literals(mapping)?,
933 ),
934 })),
935 ExprNoExt::Set(v) => {
936 let mut new_v = vec![];
937 for e in v {
938 new_v.push(e.sub_entity_literals(mapping)?);
939 }
940 Ok(Expr::ExprNoExt(ExprNoExt::Set(new_v)))
941 }
942 ExprNoExt::Record(m) => {
943 let mut new_m = BTreeMap::new();
944 for (k, v) in m {
945 new_m.insert(k, v.sub_entity_literals(mapping)?);
946 }
947 Ok(Expr::ExprNoExt(ExprNoExt::Record(new_m)))
948 }
949 #[cfg(feature = "tolerant-ast")]
950 ExprNoExt::Error(_) => Err(JsonDeserializationError::ASTErrorNode),
951 },
952 Expr::ExtFuncCall(e_fn_call) => {
953 let mut new_m = HashMap::new();
954 for (k, v) in e_fn_call.call {
955 let mut new_v = vec![];
956 for e in v {
957 new_v.push(e.sub_entity_literals(mapping)?);
958 }
959 new_m.insert(k, new_v);
960 }
961 Ok(Expr::ExtFuncCall(ExtFuncCall { call: new_m }))
962 }
963 }
964 }
965
966 pub fn height(&self) -> usize {
979 let mut stack: Vec<(&Expr, usize)> = vec![(self, 0)];
982 let mut max_depth: usize = 0;
983
984 while let Some((expr, depth)) = stack.pop() {
985 max_depth = max_depth.max(depth);
986 let child_depth = depth + 1;
987
988 match expr {
989 Expr::ExprNoExt(e) => match e {
990 ExprNoExt::Value(v) => {
991 let value_height = depth + v.height();
995 max_depth = max_depth.max(value_height);
996 }
997 ExprNoExt::Var(_) | ExprNoExt::Slot(_) => {}
998 #[cfg(feature = "tolerant-ast")]
999 ExprNoExt::Error(_) => {}
1000 ExprNoExt::Not { arg }
1001 | ExprNoExt::Neg { arg }
1002 | ExprNoExt::IsEmpty { arg } => {
1003 stack.push((arg, child_depth));
1004 }
1005 ExprNoExt::Eq { left, right }
1006 | ExprNoExt::NotEq { left, right }
1007 | ExprNoExt::In { left, right }
1008 | ExprNoExt::Less { left, right }
1009 | ExprNoExt::LessEq { left, right }
1010 | ExprNoExt::Greater { left, right }
1011 | ExprNoExt::GreaterEq { left, right }
1012 | ExprNoExt::And { left, right }
1013 | ExprNoExt::Or { left, right }
1014 | ExprNoExt::Add { left, right }
1015 | ExprNoExt::Sub { left, right }
1016 | ExprNoExt::Mul { left, right }
1017 | ExprNoExt::Contains { left, right }
1018 | ExprNoExt::ContainsAll { left, right }
1019 | ExprNoExt::ContainsAny { left, right }
1020 | ExprNoExt::GetTag { left, right }
1021 | ExprNoExt::HasTag { left, right } => {
1022 stack.push((left, child_depth));
1023 stack.push((right, child_depth));
1024 }
1025 ExprNoExt::GetAttr { left, .. } => {
1026 stack.push((left, child_depth));
1027 }
1028 ExprNoExt::HasAttr(repr) => match repr {
1029 HasAttrRepr::Simple { left, .. } | HasAttrRepr::Extended { left, .. } => {
1030 stack.push((left, child_depth));
1038 }
1039 },
1040 ExprNoExt::Like { left, .. } => {
1041 stack.push((left, child_depth));
1042 }
1043 ExprNoExt::Is { left, in_expr, .. } => {
1044 stack.push((left, child_depth));
1045 if let Some(in_e) = in_expr {
1046 stack.push((in_e, child_depth));
1047 }
1048 }
1049 ExprNoExt::If {
1050 cond_expr,
1051 then_expr,
1052 else_expr,
1053 } => {
1054 stack.push((cond_expr, child_depth));
1055 stack.push((then_expr, child_depth));
1056 stack.push((else_expr, child_depth));
1057 }
1058 ExprNoExt::Set(elements) => {
1059 for el in elements {
1060 stack.push((el, child_depth));
1061 }
1062 }
1063 ExprNoExt::Record(map) => {
1064 for v in map.values() {
1065 stack.push((v, child_depth));
1066 }
1067 }
1068 },
1069 Expr::ExtFuncCall(ExtFuncCall { call }) => {
1070 for args in call.values() {
1071 for arg in args {
1072 stack.push((arg, child_depth));
1073 }
1074 }
1075 }
1076 }
1077 }
1078
1079 max_depth
1080 }
1081}
1082
1083impl Expr {
1084 pub fn try_into_expr<B>(self) -> Result<B::Expr, B::BuildError>
1087 where
1088 B: ExprBuilder,
1089 B::BuildError: From<FromJsonError>,
1090 {
1091 let builder = B::new();
1099 match self {
1100 Expr::ExprNoExt(ExprNoExt::Value(jsonvalue)) => {
1101 let ast_expr: ast::Expr = jsonvalue
1103 .into_expr(&|| JsonDeserializationErrorContext::Policy {
1104 id: ast::PolicyID::from_string(""),
1105 })
1106 .map(ast::Expr::from)
1107 .map_err(|j| B::BuildError::from(j.into()))?;
1108 ast_expr.try_into_expr::<B>()
1109 }
1110 Expr::ExprNoExt(ExprNoExt::Var(var)) => Ok(builder.var(var)),
1111 Expr::ExprNoExt(ExprNoExt::Slot(slot)) => Ok(builder.slot(slot)),
1112 Expr::ExprNoExt(ExprNoExt::Not { arg }) => {
1113 Ok(builder.not(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
1114 }
1115 Expr::ExprNoExt(ExprNoExt::Neg { arg }) => {
1116 Ok(builder.neg(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
1117 }
1118 Expr::ExprNoExt(ExprNoExt::Eq { left, right }) => Ok(builder.is_eq(
1119 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1120 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1121 )),
1122 Expr::ExprNoExt(ExprNoExt::NotEq { left, right }) => Ok(builder.noteq(
1123 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1124 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1125 )),
1126 Expr::ExprNoExt(ExprNoExt::In { left, right }) => Ok(builder.is_in(
1127 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1128 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1129 )),
1130 Expr::ExprNoExt(ExprNoExt::Less { left, right }) => Ok(builder.less(
1131 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1132 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1133 )),
1134 Expr::ExprNoExt(ExprNoExt::LessEq { left, right }) => Ok(builder.lesseq(
1135 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1136 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1137 )),
1138 Expr::ExprNoExt(ExprNoExt::Greater { left, right }) => Ok(builder.greater(
1139 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1140 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1141 )),
1142 Expr::ExprNoExt(ExprNoExt::GreaterEq { left, right }) => Ok(builder.greatereq(
1143 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1144 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1145 )),
1146 Expr::ExprNoExt(ExprNoExt::And { left, right }) => Ok(builder.and(
1147 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1148 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1149 )),
1150 Expr::ExprNoExt(ExprNoExt::Or { left, right }) => Ok(builder.or(
1151 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1152 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1153 )),
1154 Expr::ExprNoExt(ExprNoExt::Add { left, right }) => Ok(builder.add(
1155 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1156 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1157 )),
1158 Expr::ExprNoExt(ExprNoExt::Sub { left, right }) => Ok(builder.sub(
1159 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1160 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1161 )),
1162 Expr::ExprNoExt(ExprNoExt::Mul { left, right }) => Ok(builder.mul(
1163 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1164 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1165 )),
1166 Expr::ExprNoExt(ExprNoExt::Contains { left, right }) => Ok(builder.contains(
1167 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1168 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1169 )),
1170 Expr::ExprNoExt(ExprNoExt::ContainsAll { left, right }) => Ok(builder.contains_all(
1171 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1172 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1173 )),
1174 Expr::ExprNoExt(ExprNoExt::ContainsAny { left, right }) => Ok(builder.contains_any(
1175 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1176 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1177 )),
1178 Expr::ExprNoExt(ExprNoExt::IsEmpty { arg }) => {
1179 Ok(builder.is_empty(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
1180 }
1181 Expr::ExprNoExt(ExprNoExt::GetTag { left, right }) => Ok(builder.get_tag(
1182 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1183 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1184 )),
1185 Expr::ExprNoExt(ExprNoExt::HasTag { left, right }) => Ok(builder.has_tag(
1186 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1187 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1188 )),
1189 Expr::ExprNoExt(ExprNoExt::GetAttr { left, attr }) => {
1190 Ok(builder.get_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr))
1191 }
1192 Expr::ExprNoExt(ExprNoExt::HasAttr(repr)) => match repr {
1193 HasAttrRepr::Simple { left, attr } => {
1194 Ok(builder.has_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr))
1195 }
1196 HasAttrRepr::Extended { left, attr } => Ok(builder
1197 .extended_has_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr)),
1198 },
1199 Expr::ExprNoExt(ExprNoExt::Like { left, pattern }) => Ok(builder.like(
1200 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1201 ast::Pattern::from(pattern.as_slice()),
1202 )),
1203 Expr::ExprNoExt(ExprNoExt::Is {
1204 left,
1205 entity_type,
1206 in_expr,
1207 }) => {
1208 let entity_type_name = ast::EntityType::from_normalized_str(&entity_type)
1209 .map_err(FromJsonError::InvalidEntityType)?;
1210 let left_expr = Arc::unwrap_or_clone(left).try_into_expr::<B>()?;
1211 match in_expr {
1212 Some(in_expr) => Ok(builder.is_in_entity_type(
1213 left_expr,
1214 entity_type_name,
1215 Arc::unwrap_or_clone(in_expr).try_into_expr::<B>()?,
1216 )),
1217 None => Ok(builder.is_entity_type(left_expr, entity_type_name)),
1218 }
1219 }
1220 Expr::ExprNoExt(ExprNoExt::If {
1221 cond_expr,
1222 then_expr,
1223 else_expr,
1224 }) => Ok(builder.ite(
1225 Arc::unwrap_or_clone(cond_expr).try_into_expr::<B>()?,
1226 Arc::unwrap_or_clone(then_expr).try_into_expr::<B>()?,
1227 Arc::unwrap_or_clone(else_expr).try_into_expr::<B>()?,
1228 )),
1229 Expr::ExprNoExt(ExprNoExt::Set(elements)) => Ok(builder.set(
1230 elements
1231 .into_iter()
1232 .map(|el| el.try_into_expr::<B>())
1233 .collect::<Result<Vec<_>, _>>()?,
1234 )),
1235 Expr::ExprNoExt(ExprNoExt::Record(map)) =>
1236 {
1237 #[expect(
1238 clippy::expect_used,
1239 reason = "can't have duplicate keys here because the input was already a HashMap"
1240 )]
1241 Ok(builder
1242 .record(
1243 map.into_iter()
1244 .map(|(k, v)| Ok((k, v.try_into_expr::<B>()?)))
1245 .collect::<Result<Vec<_>, B::BuildError>>()?,
1246 )
1247 .expect("map should not have duplicate keys"))
1248 }
1249 Expr::ExtFuncCall(e) => {
1250 let (fn_name, args) = e.try_into_components()?;
1251 let fn_name = Name::from_normalized_str(&fn_name).map_err(|errs| {
1252 JsonDeserializationError::parse_escape(EscapeKind::Extension, fn_name, errs)
1253 .into()
1254 })?;
1255 builder.call_extension_fn(
1258 fn_name,
1259 args.into_iter()
1260 .map(|arg| arg.try_into_expr::<B>())
1261 .collect::<Result<Vec<_>, _>>()?,
1262 )
1263 }
1264 #[cfg(feature = "tolerant-ast")]
1265 Expr::ExprNoExt(ExprNoExt::Error(_)) => Err(FromJsonError::ASTErrorNode.into()),
1266 }
1267 }
1268
1269 pub fn try_into_ast(self, id: &ast::PolicyID) -> Result<ast::Expr, FromJsonError> {
1273 match self {
1274 Expr::ExprNoExt(ExprNoExt::Value(jsonvalue)) => jsonvalue
1275 .into_expr(&|| JsonDeserializationErrorContext::Policy { id: id.clone() })
1276 .map(Into::into)
1277 .map_err(Into::into),
1278 Expr::ExprNoExt(ExprNoExt::Var(var)) => Ok(ast::Expr::var(var)),
1279 Expr::ExprNoExt(ExprNoExt::Slot(slot)) => Ok(ast::Expr::slot(slot)),
1280 Expr::ExprNoExt(ExprNoExt::Not { arg }) => {
1281 Ok(ast::Expr::not(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
1282 }
1283 Expr::ExprNoExt(ExprNoExt::Neg { arg }) => {
1284 Ok(ast::Expr::neg(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
1285 }
1286 Expr::ExprNoExt(ExprNoExt::Eq { left, right }) => Ok(ast::Expr::is_eq(
1287 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1288 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1289 )),
1290 Expr::ExprNoExt(ExprNoExt::NotEq { left, right }) => Ok(ast::Expr::noteq(
1291 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1292 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1293 )),
1294 Expr::ExprNoExt(ExprNoExt::In { left, right }) => Ok(ast::Expr::is_in(
1295 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1296 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1297 )),
1298 Expr::ExprNoExt(ExprNoExt::Less { left, right }) => Ok(ast::Expr::less(
1299 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1300 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1301 )),
1302 Expr::ExprNoExt(ExprNoExt::LessEq { left, right }) => Ok(ast::Expr::lesseq(
1303 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1304 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1305 )),
1306 Expr::ExprNoExt(ExprNoExt::Greater { left, right }) => Ok(ast::Expr::greater(
1307 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1308 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1309 )),
1310 Expr::ExprNoExt(ExprNoExt::GreaterEq { left, right }) => Ok(ast::Expr::greatereq(
1311 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1312 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1313 )),
1314 Expr::ExprNoExt(ExprNoExt::And { left, right }) => Ok(ast::Expr::and(
1315 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1316 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1317 )),
1318 Expr::ExprNoExt(ExprNoExt::Or { left, right }) => Ok(ast::Expr::or(
1319 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1320 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1321 )),
1322 Expr::ExprNoExt(ExprNoExt::Add { left, right }) => Ok(ast::Expr::add(
1323 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1324 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1325 )),
1326 Expr::ExprNoExt(ExprNoExt::Sub { left, right }) => Ok(ast::Expr::sub(
1327 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1328 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1329 )),
1330 Expr::ExprNoExt(ExprNoExt::Mul { left, right }) => Ok(ast::Expr::mul(
1331 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1332 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1333 )),
1334 Expr::ExprNoExt(ExprNoExt::Contains { left, right }) => Ok(ast::Expr::contains(
1335 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1336 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1337 )),
1338 Expr::ExprNoExt(ExprNoExt::ContainsAll { left, right }) => Ok(ast::Expr::contains_all(
1339 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1340 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1341 )),
1342 Expr::ExprNoExt(ExprNoExt::ContainsAny { left, right }) => Ok(ast::Expr::contains_any(
1343 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1344 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1345 )),
1346 Expr::ExprNoExt(ExprNoExt::IsEmpty { arg }) => Ok(ast::Expr::is_empty(
1347 Arc::unwrap_or_clone(arg).try_into_ast(id)?,
1348 )),
1349 Expr::ExprNoExt(ExprNoExt::GetTag { left, right }) => Ok(ast::Expr::get_tag(
1350 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1351 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1352 )),
1353 Expr::ExprNoExt(ExprNoExt::HasTag { left, right }) => Ok(ast::Expr::has_tag(
1354 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1355 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1356 )),
1357 Expr::ExprNoExt(ExprNoExt::GetAttr { left, attr }) => Ok(ast::Expr::get_attr(
1358 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1359 attr,
1360 )),
1361 Expr::ExprNoExt(ExprNoExt::HasAttr(repr)) => match repr {
1362 HasAttrRepr::Simple { left, attr } => Ok(ast::Expr::has_attr(
1363 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1364 attr,
1365 )),
1366 HasAttrRepr::Extended { left, attr } => Ok(ast::ExprBuilder::new()
1367 .extended_has_attr(Arc::unwrap_or_clone(left).try_into_ast(id)?, attr)),
1368 },
1369 Expr::ExprNoExt(ExprNoExt::Like { left, pattern }) => Ok(ast::Expr::like(
1370 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1371 crate::ast::Pattern::from(pattern.as_slice()),
1372 )),
1373 Expr::ExprNoExt(ExprNoExt::Is {
1374 left,
1375 entity_type,
1376 in_expr,
1377 }) => ast::EntityType::from_normalized_str(entity_type.as_str())
1378 .map_err(FromJsonError::InvalidEntityType)
1379 .and_then(|entity_type_name| {
1380 let left: ast::Expr = Arc::unwrap_or_clone(left).try_into_ast(id)?;
1381 match in_expr {
1382 Some(in_expr) => Ok(ast::ExprBuilder::new().is_in_entity_type(
1383 left,
1384 entity_type_name,
1385 Arc::unwrap_or_clone(in_expr).try_into_ast(id)?,
1386 )),
1387 None => Ok(ast::ExprBuilder::new().is_entity_type(left, entity_type_name)),
1388 }
1389 }),
1390 Expr::ExprNoExt(ExprNoExt::If {
1391 cond_expr,
1392 then_expr,
1393 else_expr,
1394 }) => Ok(ast::Expr::ite(
1395 Arc::unwrap_or_clone(cond_expr).try_into_ast(id)?,
1396 Arc::unwrap_or_clone(then_expr).try_into_ast(id)?,
1397 Arc::unwrap_or_clone(else_expr).try_into_ast(id)?,
1398 )),
1399 Expr::ExprNoExt(ExprNoExt::Set(elements)) => Ok(ast::Expr::set(
1400 elements
1401 .into_iter()
1402 .map(|el| el.try_into_ast(id))
1403 .collect::<Result<Vec<_>, FromJsonError>>()?,
1404 )),
1405 Expr::ExprNoExt(ExprNoExt::Record(map)) =>
1406 {
1407 #[expect(
1408 clippy::expect_used,
1409 reason = "can't have duplicate keys here because the input was already a HashMap"
1410 )]
1411 Ok(ast::Expr::record(
1412 map.into_iter()
1413 .map(|(k, v)| Ok((k, v.try_into_ast(id)?)))
1414 .collect::<Result<HashMap<SmolStr, _>, FromJsonError>>()?,
1415 )
1416 .expect("can't have duplicate keys here because the input was already a HashMap"))
1417 }
1418 Expr::ExtFuncCall(e) => {
1419 let (fn_name, args) = e.try_into_components()?;
1420 let fn_name = Name::from_normalized_str(&fn_name).map_err(|errs| {
1421 JsonDeserializationError::parse_escape(EscapeKind::Extension, fn_name, errs)
1422 })?;
1423 if ExtStyles::is_known_extension_func_name(&fn_name) {
1424 Ok(ast::Expr::call_extension_fn(
1425 fn_name,
1426 args.into_iter()
1427 .map(|arg| arg.try_into_ast(id))
1428 .collect::<Result<_, _>>()?,
1429 ))
1430 } else {
1431 Err(FromJsonError::UnknownExtensionFunction(fn_name))
1432 }
1433 }
1434 #[cfg(feature = "tolerant-ast")]
1435 Expr::ExprNoExt(ExprNoExt::Error(_)) => Err(FromJsonError::ASTErrorNode),
1436 }
1437 }
1438}
1439
1440impl From<ast::Literal> for Expr {
1441 fn from(lit: ast::Literal) -> Expr {
1442 Builder::new().val(lit)
1443 }
1444}
1445
1446impl From<ast::Var> for Expr {
1447 fn from(var: ast::Var) -> Expr {
1448 Builder::new().var(var)
1449 }
1450}
1451
1452impl From<ast::SlotId> for Expr {
1453 fn from(slot: ast::SlotId) -> Expr {
1454 Builder::new().slot(slot)
1455 }
1456}
1457
1458impl TryFrom<&Node<Option<cst::Expr>>> for Expr {
1459 type Error = ParseErrors;
1460 fn try_from(e: &Node<Option<cst::Expr>>) -> Result<Expr, ParseErrors> {
1461 e.to_expr::<Builder>()
1462 }
1463}
1464
1465impl std::fmt::Display for Expr {
1466 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1467 match self {
1468 Self::ExprNoExt(e) => write!(f, "{e}"),
1469 Self::ExtFuncCall(e) => write!(f, "{e}"),
1470 }
1471 }
1472}
1473
1474impl BoundedDisplay for Expr {
1475 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1476 match self {
1477 Self::ExprNoExt(e) => BoundedDisplay::fmt(e, f, n),
1478 Self::ExtFuncCall(e) => BoundedDisplay::fmt(e, f, n),
1479 }
1480 }
1481}
1482
1483fn display_cedarvaluejson(
1484 f: &mut impl std::fmt::Write,
1485 v: &CedarValueJson,
1486 n: Option<usize>,
1487) -> std::fmt::Result {
1488 match v {
1489 CedarValueJson::Long(i) if *i < 0 => write!(f, "({i})"),
1492 CedarValueJson::Long(i) => write!(f, "{i}"),
1493 CedarValueJson::Bool(b) => write!(f, "{b}"),
1494 CedarValueJson::String(s) => write!(f, "\"{}\"", s.escape_debug()),
1495 CedarValueJson::EntityEscape { __entity } => {
1496 match ast::EntityUID::try_from(__entity.clone()) {
1497 Ok(euid) => write!(f, "{euid}"),
1498 Err(e) => write!(f, "(invalid entity uid: {e})"),
1499 }
1500 }
1501 CedarValueJson::ExprEscape { __expr } => write!(f, "({__expr})"),
1502 CedarValueJson::ExtnEscape {
1503 __extn: FnAndArgs::Single { ext_fn, arg },
1504 } => {
1505 let style = Extensions::all_available().all_funcs().find_map(|f| {
1507 if &f.name().to_smolstr() == ext_fn {
1508 Some(f.style())
1509 } else {
1510 None
1511 }
1512 });
1513 match style {
1514 Some(ast::CallStyle::MethodStyle) => {
1515 display_cedarvaluejson(f, arg, n)?;
1516 write!(f, ".{ext_fn}()")?;
1517 Ok(())
1518 }
1519 Some(ast::CallStyle::FunctionStyle) | None => {
1520 write!(f, "{ext_fn}(")?;
1521 display_cedarvaluejson(f, arg, n)?;
1522 write!(f, ")")?;
1523 Ok(())
1524 }
1525 }
1526 }
1527 CedarValueJson::ExtnEscape {
1528 __extn: FnAndArgs::Multi { ext_fn, args },
1529 } => {
1530 let style = Extensions::all_available().all_funcs().find_map(|f| {
1532 if &f.name().to_smolstr() == ext_fn {
1533 Some(f.style())
1534 } else {
1535 None
1536 }
1537 });
1538 match style {
1539 Some(ast::CallStyle::MethodStyle) => {
1540 #[expect(
1541 clippy::indexing_slicing,
1542 reason = "method-style calls must have more than one argument"
1543 )]
1544 display_cedarvaluejson(f, &args[0], n)?;
1545 write!(f, ".{ext_fn}(")?;
1546 #[expect(
1547 clippy::indexing_slicing,
1548 reason = "method-style calls must have more than one argument"
1549 )]
1550 match &args[1..] {
1551 [] => {}
1552 [args @ .., last] => {
1553 for arg in args {
1554 display_cedarvaluejson(f, arg, n)?;
1555 write!(f, ", ")?;
1556 }
1557 display_cedarvaluejson(f, last, n)?;
1558 }
1559 }
1560 write!(f, ")")?;
1561 Ok(())
1562 }
1563 Some(ast::CallStyle::FunctionStyle) | None => {
1564 write!(f, "{ext_fn}(")?;
1565 match &args[..] {
1566 [] => {}
1567 [args @ .., last] => {
1568 for arg in args {
1569 display_cedarvaluejson(f, arg, n)?;
1570 write!(f, ", ")?;
1571 }
1572 display_cedarvaluejson(f, last, n)?;
1573 }
1574 }
1575 write!(f, ")")?;
1576 Ok(())
1577 }
1578 }
1579 }
1580 CedarValueJson::Set(v) => {
1581 match n {
1582 Some(n) if v.len() > n => {
1583 write!(f, "[")?;
1585 for val in v.iter().take(n) {
1586 display_cedarvaluejson(f, val, Some(n))?;
1587 write!(f, ", ")?;
1588 }
1589 write!(f, "..]")?;
1590 Ok(())
1591 }
1592 _ => {
1593 write!(f, "[")?;
1595 for (i, val) in v.iter().enumerate() {
1596 display_cedarvaluejson(f, val, n)?;
1597 if i < v.len() - 1 {
1598 write!(f, ", ")?;
1599 }
1600 }
1601 write!(f, "]")?;
1602 Ok(())
1603 }
1604 }
1605 }
1606 CedarValueJson::Record(r) => {
1607 match n {
1608 Some(n) if r.len() > n => {
1609 write!(f, "{{")?;
1611 for (k, v) in r.iter().take(n) {
1612 write!(f, "\"{}\": ", k.escape_debug())?;
1613 display_cedarvaluejson(f, v, Some(n))?;
1614 write!(f, ", ")?;
1615 }
1616 write!(f, "..}}")?;
1617 Ok(())
1618 }
1619 _ => {
1620 write!(f, "{{")?;
1622 for (i, (k, v)) in r.iter().enumerate() {
1623 write!(f, "\"{}\": ", k.escape_debug())?;
1624 display_cedarvaluejson(f, v, n)?;
1625 if i < r.len() - 1 {
1626 write!(f, ", ")?;
1627 }
1628 }
1629 write!(f, "}}")?;
1630 Ok(())
1631 }
1632 }
1633 }
1634 CedarValueJson::Null => {
1635 write!(f, "null")?;
1636 Ok(())
1637 }
1638 }
1639}
1640
1641impl std::fmt::Display for ExprNoExt {
1642 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1643 BoundedDisplay::fmt_unbounded(self, f)
1644 }
1645}
1646
1647impl BoundedDisplay for ExprNoExt {
1648 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1649 match &self {
1650 ExprNoExt::Value(v) => display_cedarvaluejson(f, v, n),
1651 ExprNoExt::Var(v) => write!(f, "{v}"),
1652 ExprNoExt::Slot(id) => write!(f, "{id}"),
1653 ExprNoExt::Not { arg } => {
1654 write!(f, "!")?;
1655 maybe_with_parens(f, arg, n)
1656 }
1657 ExprNoExt::Neg { arg } => {
1658 write!(f, "-({arg})")
1664 }
1665 ExprNoExt::Eq { left, right } => {
1666 maybe_with_parens(f, left, n)?;
1667 write!(f, " == ")?;
1668 maybe_with_parens(f, right, n)
1669 }
1670 ExprNoExt::NotEq { left, right } => {
1671 maybe_with_parens(f, left, n)?;
1672 write!(f, " != ")?;
1673 maybe_with_parens(f, right, n)
1674 }
1675 ExprNoExt::In { left, right } => {
1676 maybe_with_parens(f, left, n)?;
1677 write!(f, " in ")?;
1678 maybe_with_parens(f, right, n)
1679 }
1680 ExprNoExt::Less { left, right } => {
1681 maybe_with_parens(f, left, n)?;
1682 write!(f, " < ")?;
1683 maybe_with_parens(f, right, n)
1684 }
1685 ExprNoExt::LessEq { left, right } => {
1686 maybe_with_parens(f, left, n)?;
1687 write!(f, " <= ")?;
1688 maybe_with_parens(f, right, n)
1689 }
1690 ExprNoExt::Greater { left, right } => {
1691 maybe_with_parens(f, left, n)?;
1692 write!(f, " > ")?;
1693 maybe_with_parens(f, right, n)
1694 }
1695 ExprNoExt::GreaterEq { left, right } => {
1696 maybe_with_parens(f, left, n)?;
1697 write!(f, " >= ")?;
1698 maybe_with_parens(f, right, n)
1699 }
1700 ExprNoExt::And { left, right } => {
1701 maybe_with_parens(f, left, n)?;
1702 write!(f, " && ")?;
1703 maybe_with_parens(f, right, n)
1704 }
1705 ExprNoExt::Or { left, right } => {
1706 maybe_with_parens(f, left, n)?;
1707 write!(f, " || ")?;
1708 maybe_with_parens(f, right, n)
1709 }
1710 ExprNoExt::Add { left, right } => {
1711 maybe_with_parens(f, left, n)?;
1712 write!(f, " + ")?;
1713 maybe_with_parens(f, right, n)
1714 }
1715 ExprNoExt::Sub { left, right } => {
1716 maybe_with_parens(f, left, n)?;
1717 write!(f, " - ")?;
1718 maybe_with_parens(f, right, n)
1719 }
1720 ExprNoExt::Mul { left, right } => {
1721 maybe_with_parens(f, left, n)?;
1722 write!(f, " * ")?;
1723 maybe_with_parens(f, right, n)
1724 }
1725 ExprNoExt::Contains { left, right } => {
1726 maybe_with_parens(f, left, n)?;
1727 write!(f, ".contains({right})")
1728 }
1729 ExprNoExt::ContainsAll { left, right } => {
1730 maybe_with_parens(f, left, n)?;
1731 write!(f, ".containsAll({right})")
1732 }
1733 ExprNoExt::ContainsAny { left, right } => {
1734 maybe_with_parens(f, left, n)?;
1735 write!(f, ".containsAny({right})")
1736 }
1737 ExprNoExt::IsEmpty { arg } => {
1738 maybe_with_parens(f, arg, n)?;
1739 write!(f, ".isEmpty()")
1740 }
1741 ExprNoExt::GetTag { left, right } => {
1742 maybe_with_parens(f, left, n)?;
1743 write!(f, ".getTag({right})")
1744 }
1745 ExprNoExt::HasTag { left, right } => {
1746 maybe_with_parens(f, left, n)?;
1747 write!(f, ".hasTag({right})")
1748 }
1749 ExprNoExt::GetAttr { left, attr } => {
1750 maybe_with_parens(f, left, n)?;
1751 if is_normalized_ident(attr) {
1752 write!(f, ".{}", attr)
1753 } else {
1754 write!(f, "[\"{}\"]", attr.escape_debug())
1755 }
1756 }
1757 ExprNoExt::HasAttr(repr) => match repr {
1758 HasAttrRepr::Simple { left, attr } => {
1759 maybe_with_parens(f, left, n)?;
1760 if is_normalized_ident(attr) {
1761 write!(f, " has {}", attr)
1762 } else {
1763 write!(f, " has \"{}\"", attr.escape_debug())
1764 }
1765 }
1766 HasAttrRepr::Extended { left, attr } => {
1767 maybe_with_parens(f, left, n)?;
1768 if is_normalized_ident(&attr.head) {
1769 write!(f, " has {}", attr.head)?;
1770 } else {
1771 write!(f, " has \"{}\"", attr.head.escape_debug())?;
1772 }
1773 for attr in attr.tail.iter() {
1774 if is_normalized_ident(attr) {
1777 write!(f, ".{}", attr)?;
1778 } else {
1779 write!(f, ".\"{}\"", attr.escape_debug())?;
1780 }
1781 }
1782 Ok(())
1783 }
1784 },
1785 ExprNoExt::Like { left, pattern } => {
1786 maybe_with_parens(f, left, n)?;
1787 write!(
1788 f,
1789 " like \"{}\"",
1790 crate::ast::Pattern::from(pattern.as_slice())
1791 )
1792 }
1793 ExprNoExt::Is {
1794 left,
1795 entity_type,
1796 in_expr,
1797 } => {
1798 maybe_with_parens(f, left, n)?;
1799 write!(f, " is {entity_type}")?;
1800 match in_expr {
1801 Some(in_expr) => {
1802 write!(f, " in ")?;
1803 maybe_with_parens(f, in_expr, n)
1804 }
1805 None => Ok(()),
1806 }
1807 }
1808 ExprNoExt::If {
1809 cond_expr,
1810 then_expr,
1811 else_expr,
1812 } => {
1813 write!(f, "if ")?;
1814 maybe_with_parens(f, cond_expr, n)?;
1815 write!(f, " then ")?;
1816 maybe_with_parens(f, then_expr, n)?;
1817 write!(f, " else ")?;
1818 maybe_with_parens(f, else_expr, n)
1819 }
1820 ExprNoExt::Set(v) => {
1821 match n {
1822 Some(n) if v.len() > n => {
1823 write!(f, "[")?;
1825 for element in v.iter().take(n) {
1826 BoundedDisplay::fmt(element, f, Some(n))?;
1827 write!(f, ", ")?;
1828 }
1829 write!(f, "..]")?;
1830 Ok(())
1831 }
1832 _ => {
1833 write!(f, "[")?;
1835 for (i, element) in v.iter().enumerate() {
1836 BoundedDisplay::fmt(element, f, n)?;
1837 if i < v.len() - 1 {
1838 write!(f, ", ")?;
1839 }
1840 }
1841 write!(f, "]")?;
1842 Ok(())
1843 }
1844 }
1845 }
1846 ExprNoExt::Record(m) => {
1847 match n {
1848 Some(n) if m.len() > n => {
1849 write!(f, "{{")?;
1851 for (k, v) in m.iter().take(n) {
1852 if is_normalized_ident(k) {
1853 write!(f, "{k}: ")?;
1854 } else {
1855 write!(f, "\"{}\": ", k.escape_debug())?;
1856 }
1857 BoundedDisplay::fmt(v, f, Some(n))?;
1858 write!(f, ", ")?;
1859 }
1860 write!(f, "..}}")?;
1861 Ok(())
1862 }
1863 _ => {
1864 write!(f, "{{")?;
1866 for (i, (k, v)) in m.iter().enumerate() {
1867 if is_normalized_ident(k) {
1868 write!(f, "{k}: ")?;
1869 } else {
1870 write!(f, "\"{}\": ", k.escape_debug())?;
1871 }
1872 BoundedDisplay::fmt(v, f, n)?;
1873 if i < m.len() - 1 {
1874 write!(f, ", ")?;
1875 }
1876 }
1877 write!(f, "}}")?;
1878 Ok(())
1879 }
1880 }
1881 }
1882 #[cfg(feature = "tolerant-ast")]
1883 ExprNoExt::Error(e) => {
1884 write!(f, "{e}")?;
1885 Ok(())
1886 }
1887 }
1888 }
1889}
1890
1891impl std::fmt::Display for ExtFuncCall {
1892 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1893 BoundedDisplay::fmt_unbounded(self, f)
1894 }
1895}
1896
1897impl BoundedDisplay for ExtFuncCall {
1898 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1899 #[expect(clippy::unwrap_used, reason = "safe due to INVARIANT on `ExtFuncCall`")]
1900 let (fn_name, args) = self.try_components().unwrap();
1901 let style = Extensions::all_available().all_funcs().find_map(|ext_fn| {
1903 if &ext_fn.name().to_smolstr() == fn_name {
1904 Some(ext_fn.style())
1905 } else {
1906 None
1907 }
1908 });
1909 match (style, args) {
1910 (Some(ast::CallStyle::MethodStyle), [receiver, rest @ ..]) => {
1911 maybe_with_parens(f, receiver, n)?;
1912 write!(f, ".{}({})", fn_name, rest.iter().join(", "))
1913 }
1914 (_, _) => {
1915 write!(f, "{}({})", fn_name, args.iter().join(", "))
1916 }
1917 }
1918 }
1919}
1920
1921fn maybe_with_parens(
1927 f: &mut impl std::fmt::Write,
1928 expr: &Expr,
1929 n: Option<usize>,
1930) -> std::fmt::Result {
1931 match expr {
1932 Expr::ExprNoExt(ExprNoExt::Set(_)) |
1933 Expr::ExprNoExt(ExprNoExt::Record(_)) |
1934 Expr::ExprNoExt(ExprNoExt::Value(_)) |
1935 Expr::ExprNoExt(ExprNoExt::Var(_)) |
1936 Expr::ExprNoExt(ExprNoExt::Slot(_)) => BoundedDisplay::fmt(expr, f, n),
1937
1938 Expr::ExprNoExt(ExprNoExt::Not { .. }) |
1941 Expr::ExprNoExt(ExprNoExt::Neg { .. }) |
1944 Expr::ExprNoExt(ExprNoExt::Eq { .. }) |
1945 Expr::ExprNoExt(ExprNoExt::NotEq { .. }) |
1946 Expr::ExprNoExt(ExprNoExt::In { .. }) |
1947 Expr::ExprNoExt(ExprNoExt::Less { .. }) |
1948 Expr::ExprNoExt(ExprNoExt::LessEq { .. }) |
1949 Expr::ExprNoExt(ExprNoExt::Greater { .. }) |
1950 Expr::ExprNoExt(ExprNoExt::GreaterEq { .. }) |
1951 Expr::ExprNoExt(ExprNoExt::And { .. }) |
1952 Expr::ExprNoExt(ExprNoExt::Or { .. }) |
1953 Expr::ExprNoExt(ExprNoExt::Add { .. }) |
1954 Expr::ExprNoExt(ExprNoExt::Sub { .. }) |
1955 Expr::ExprNoExt(ExprNoExt::Mul { .. }) |
1956 Expr::ExprNoExt(ExprNoExt::Contains { .. }) |
1957 Expr::ExprNoExt(ExprNoExt::ContainsAll { .. }) |
1958 Expr::ExprNoExt(ExprNoExt::ContainsAny { .. }) |
1959 Expr::ExprNoExt(ExprNoExt::IsEmpty { .. }) |
1960 Expr::ExprNoExt(ExprNoExt::GetAttr { .. }) |
1961 Expr::ExprNoExt(ExprNoExt::HasAttr { .. }) |
1962 Expr::ExprNoExt(ExprNoExt::GetTag { .. }) |
1963 Expr::ExprNoExt(ExprNoExt::HasTag { .. }) |
1964 Expr::ExprNoExt(ExprNoExt::Like { .. }) |
1965 Expr::ExprNoExt(ExprNoExt::Is { .. }) |
1966 Expr::ExprNoExt(ExprNoExt::If { .. }) |
1967 Expr::ExtFuncCall { .. } => {
1968 write!(f, "(")?;
1969 BoundedDisplay::fmt(expr, f, n)?;
1970 write!(f, ")")?;
1971 Ok(())
1972 },
1973 #[cfg(feature = "tolerant-ast")]
1974 Expr::ExprNoExt(ExprNoExt::Error { .. }) => {
1975 write!(f, "(")?;
1976 BoundedDisplay::fmt(expr, f, n)?;
1977 write!(f, ")")?;
1978 Ok(())
1979 }
1980 }
1981}
1982
1983#[cfg(test)]
1984#[expect(clippy::indexing_slicing, reason = "this is unit test code")]
1985#[expect(clippy::panic, reason = "Unit Test Code")]
1986mod test {
1987 use crate::parser::{
1988 err::{ParseError, ToASTErrorKind},
1989 parse_expr,
1990 };
1991
1992 use super::*;
1993 use ast::BoundedToString;
1994 use cool_asserts::assert_matches;
1995
1996 #[test]
1997 fn test_invalid_expr_from_cst_name() {
1998 let e = crate::parser::text_to_cst::parse_expr("some_long_str::else").unwrap();
1999 assert_matches!(Expr::try_from(&e), Err(e) => {
2000 assert!(e.len() == 1);
2001 assert_matches!(&e[0],
2002 ParseError::ToAST(to_ast_error) => {
2003 assert_matches!(to_ast_error.kind(), ToASTErrorKind::ReservedIdentifier(s) => {
2004 assert_eq!(s.to_string(), "else");
2005 });
2006 }
2007 );
2008 });
2009 }
2010
2011 #[test]
2012 fn display_and_bounded_display() {
2013 let expr = parse_expr(r#"[100, [3, 4, 5], -20, "foo"]"#)
2014 .unwrap()
2015 .into_expr::<Builder>();
2016 assert_eq!(format!("{expr}"), r#"[100, [3, 4, 5], (-20), "foo"]"#);
2017 assert_eq!(
2018 BoundedToString::to_string(&expr, None),
2019 r#"[100, [3, 4, 5], (-20), "foo"]"#
2020 );
2021 assert_eq!(
2022 BoundedToString::to_string(&expr, Some(4)),
2023 r#"[100, [3, 4, 5], (-20), "foo"]"#
2024 );
2025 assert_eq!(
2026 BoundedToString::to_string(&expr, Some(3)),
2027 r#"[100, [3, 4, 5], (-20), ..]"#
2028 );
2029 assert_eq!(
2030 BoundedToString::to_string(&expr, Some(2)),
2031 r#"[100, [3, 4, ..], ..]"#
2032 );
2033 assert_eq!(BoundedToString::to_string(&expr, Some(1)), r#"[100, ..]"#);
2034 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"[..]"#);
2035
2036 let expr = parse_expr(
2037 r#"{
2038 a: 12,
2039 b: [3, 4, true],
2040 c: -20,
2041 "hello ∞ world": "∂µß≈¥"
2042 }"#,
2043 )
2044 .unwrap()
2045 .into_expr::<Builder>();
2046 assert_eq!(
2047 format!("{expr}"),
2048 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
2049 );
2050 assert_eq!(
2051 BoundedToString::to_string(&expr, None),
2052 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
2053 );
2054 assert_eq!(
2055 BoundedToString::to_string(&expr, Some(4)),
2056 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
2057 );
2058 assert_eq!(
2059 BoundedToString::to_string(&expr, Some(3)),
2060 r#"{a: 12, b: [3, 4, true], c: (-20), ..}"#
2061 );
2062 assert_eq!(
2063 BoundedToString::to_string(&expr, Some(2)),
2064 r#"{a: 12, b: [3, 4, ..], ..}"#
2065 );
2066 assert_eq!(BoundedToString::to_string(&expr, Some(1)), r#"{a: 12, ..}"#);
2067 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"{..}"#);
2068 }
2069
2070 #[test]
2071 fn display_get_attr() {
2072 let expr = parse_expr(r#"context.foo"#).unwrap().into_expr::<Builder>();
2074 assert_eq!(format!("{expr}"), r#"context.foo"#);
2075
2076 let expr = parse_expr(r#"context["foo"]"#)
2077 .unwrap()
2078 .into_expr::<Builder>();
2079 assert_eq!(format!("{expr}"), r#"context.foo"#);
2080
2081 let expr = parse_expr(r#"context["foo "]"#)
2083 .unwrap()
2084 .into_expr::<Builder>();
2085 assert_eq!(format!("{expr}"), r#"context["foo "]"#);
2086
2087 let expr = parse_expr(r#"context["foo-baz"]"#)
2088 .unwrap()
2089 .into_expr::<Builder>();
2090 assert_eq!(format!("{expr}"), r#"context["foo-baz"]"#);
2091
2092 let expr = parse_expr(r#"context["true"]"#)
2093 .unwrap()
2094 .into_expr::<Builder>();
2095 assert_eq!(format!("{expr}"), r#"context["true"]"#);
2096
2097 let expr = parse_expr(r#"context has foo"#)
2099 .unwrap()
2100 .into_expr::<Builder>();
2101 assert_eq!(format!("{expr}"), r#"context has foo"#);
2102
2103 let expr = parse_expr(r#"context has "foo""#)
2104 .unwrap()
2105 .into_expr::<Builder>();
2106 assert_eq!(format!("{expr}"), r#"context has foo"#);
2107
2108 let expr = parse_expr(r#"context has "foo-baz""#)
2109 .unwrap()
2110 .into_expr::<Builder>();
2111 assert_eq!(format!("{expr}"), r#"context has "foo-baz""#);
2112
2113 let expr = parse_expr(r#"if context has "if" then false else true"#)
2114 .unwrap()
2115 .into_expr::<Builder>();
2116 assert_eq!(
2117 format!("{expr}"),
2118 r#"if (context has "if") then false else true"#
2119 );
2120
2121 let expr = parse_expr(r#"context has "has""#)
2122 .unwrap()
2123 .into_expr::<Builder>();
2124 assert_eq!(format!("{expr}"), r#"context has "has""#);
2125
2126 let expr = parse_expr(r#"if context has "foo-baz" then false else true"#)
2127 .unwrap()
2128 .into_expr::<Builder>();
2129 assert_eq!(
2130 format!("{expr}"),
2131 r#"if (context has "foo-baz") then false else true"#
2132 );
2133 }
2134
2135 #[test]
2136 fn has_attr_serde() {
2137 use nonempty::nonempty;
2138 let test_cases = vec![
2139 (
2141 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
2142 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2143 attr: "department".into(),
2144 })),
2145 serde_json::json!({"has": {"left": {"Var": "principal"}, "attr": "department"}}),
2146 ),
2147 (
2149 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2150 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2151 attr: nonempty!["user".into()],
2152 })),
2153 serde_json::json!({"has": {"left": {"Var": "context"}, "attr": ["user"]}}),
2154 ),
2155 (
2157 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2158 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2159 attr: nonempty!["user".into(), "profile".into(), "email".into()],
2160 })),
2161 serde_json::json!({"has": {"left": {"Var": "context"}, "attr": ["user", "profile", "email"]}}),
2162 ),
2163 ];
2164 for (expr, json_repr) in test_cases {
2165 assert_eq!(serde_json::to_value(&expr).unwrap(), json_repr);
2167 assert_eq!(expr, serde_json::from_value(json_repr).unwrap());
2169 }
2170 }
2171
2172 #[test]
2173 fn deserialize_has_attr_empty_errors() {
2174 let json = serde_json::json!({"has": {"left": {"Var": "context"}, "attr": []}});
2175 let expr_result: Result<Expr, _> = serde_json::from_value(json);
2176 assert!(expr_result.is_err())
2177 }
2178
2179 #[test]
2180 fn extended_has_display() {
2181 use nonempty::nonempty;
2184
2185 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
2187 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2188 attr: "department".into(),
2189 }));
2190 assert_eq!(format!("{expr}"), "principal has department");
2191
2192 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2194 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2195 attr: nonempty!["user".into()],
2196 }));
2197 assert_eq!(format!("{expr}"), "context has user");
2198
2199 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2201 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2202 attr: nonempty!["user".into(), "profile".into(), "email".into()],
2203 }));
2204 assert_eq!(format!("{expr}"), "context has user.profile.email");
2205 }
2206
2207 #[test]
2208 fn height_leaf() {
2209 let expr = Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)));
2210 assert_eq!(expr.height(), 0);
2211 let expr = Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal));
2212 assert_eq!(expr.height(), 0);
2213 }
2214
2215 #[test]
2216 fn height_unary() {
2217 let expr = Expr::ExprNoExt(ExprNoExt::Not {
2218 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2219 });
2220 assert_eq!(expr.height(), 1);
2221 }
2222
2223 #[test]
2224 fn height_binary() {
2225 let expr = Expr::ExprNoExt(ExprNoExt::Eq {
2226 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2227 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2228 });
2229 assert_eq!(expr.height(), 1);
2230 }
2231
2232 #[test]
2233 fn height_nested() {
2234 let expr = Expr::ExprNoExt(ExprNoExt::Not {
2235 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Not {
2236 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2237 })),
2238 });
2239 assert_eq!(expr.height(), 2);
2240 }
2241
2242 #[test]
2243 fn height_asymmetric() {
2244 let deep_left = Expr::ExprNoExt(ExprNoExt::Not {
2245 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Not {
2246 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2247 })),
2248 });
2249 let expr = Expr::ExprNoExt(ExprNoExt::Eq {
2250 left: Arc::new(deep_left),
2251 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2252 });
2253 assert_eq!(expr.height(), 3);
2254 }
2255
2256 #[test]
2257 fn height_if_then_else() {
2258 let expr = Expr::ExprNoExt(ExprNoExt::If {
2259 cond_expr: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2260 true,
2261 )))),
2262 then_expr: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2263 else_expr: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2264 });
2265 assert_eq!(expr.height(), 1);
2266 }
2267
2268 #[test]
2269 fn height_set() {
2270 let expr = Expr::ExprNoExt(ExprNoExt::Set(vec![
2271 Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1))),
2272 Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2))),
2273 ]));
2274 assert_eq!(expr.height(), 1);
2275 }
2276
2277 #[test]
2278 fn height_empty_set() {
2279 let expr = Expr::ExprNoExt(ExprNoExt::Set(vec![]));
2280 assert_eq!(expr.height(), 0);
2281 }
2282
2283 #[test]
2284 fn height_record() {
2285 let mut map = BTreeMap::new();
2286 map.insert(
2287 "a".into(),
2288 Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1))),
2289 );
2290 let expr = Expr::ExprNoExt(ExprNoExt::Record(map));
2291 assert_eq!(expr.height(), 1);
2292 }
2293
2294 #[test]
2295 fn height_neg() {
2296 let expr = Expr::ExprNoExt(ExprNoExt::Neg {
2298 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(5)))),
2299 });
2300 assert_eq!(expr.height(), 1);
2301
2302 let expr = Expr::ExprNoExt(ExprNoExt::Neg {
2304 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Neg {
2305 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(5)))),
2306 })),
2307 });
2308 assert_eq!(expr.height(), 2);
2309 }
2310
2311 #[test]
2312 fn height_not_eq() {
2313 let expr = Expr::ExprNoExt(ExprNoExt::NotEq {
2314 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2315 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2316 });
2317 assert_eq!(expr.height(), 1);
2318 }
2319
2320 #[test]
2321 fn height_in() {
2322 let expr = Expr::ExprNoExt(ExprNoExt::In {
2323 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2324 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Resource))),
2325 });
2326 assert_eq!(expr.height(), 1);
2327 }
2328
2329 #[test]
2330 fn height_less() {
2331 let expr = Expr::ExprNoExt(ExprNoExt::Less {
2332 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2333 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2334 });
2335 assert_eq!(expr.height(), 1);
2336 }
2337
2338 #[test]
2339 fn height_less_eq() {
2340 let expr = Expr::ExprNoExt(ExprNoExt::LessEq {
2341 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2342 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2343 });
2344 assert_eq!(expr.height(), 1);
2345 }
2346
2347 #[test]
2348 fn height_greater() {
2349 let expr = Expr::ExprNoExt(ExprNoExt::Greater {
2350 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2351 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2352 });
2353 assert_eq!(expr.height(), 1);
2354 }
2355
2356 #[test]
2357 fn height_greater_eq() {
2358 let expr = Expr::ExprNoExt(ExprNoExt::GreaterEq {
2359 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2360 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2361 });
2362 assert_eq!(expr.height(), 1);
2363 }
2364
2365 #[test]
2366 fn height_and() {
2367 let expr = Expr::ExprNoExt(ExprNoExt::And {
2368 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2369 true,
2370 )))),
2371 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2372 false,
2373 )))),
2374 });
2375 assert_eq!(expr.height(), 1);
2376 }
2377
2378 #[test]
2379 fn height_or() {
2380 let expr = Expr::ExprNoExt(ExprNoExt::Or {
2381 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2382 true,
2383 )))),
2384 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2385 false,
2386 )))),
2387 });
2388 assert_eq!(expr.height(), 1);
2389 }
2390
2391 #[test]
2392 fn height_add() {
2393 let expr = Expr::ExprNoExt(ExprNoExt::Add {
2394 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2395 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2396 });
2397 assert_eq!(expr.height(), 1);
2398 }
2399
2400 #[test]
2401 fn height_sub() {
2402 let expr = Expr::ExprNoExt(ExprNoExt::Sub {
2403 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(3)))),
2404 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2405 });
2406 assert_eq!(expr.height(), 1);
2407 }
2408
2409 #[test]
2410 fn height_mul() {
2411 let expr = Expr::ExprNoExt(ExprNoExt::Mul {
2412 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(2)))),
2413 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(3)))),
2414 });
2415 assert_eq!(expr.height(), 1);
2416 }
2417
2418 #[test]
2419 fn height_contains() {
2420 let expr = Expr::ExprNoExt(ExprNoExt::Contains {
2421 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Set(vec![Expr::ExprNoExt(
2422 ExprNoExt::Value(CedarValueJson::Long(1)),
2423 )]))),
2424 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Long(1)))),
2425 });
2426 assert_eq!(expr.height(), 2);
2427 }
2428
2429 #[test]
2430 fn height_contains_all() {
2431 let expr = Expr::ExprNoExt(ExprNoExt::ContainsAll {
2432 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2433 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Set(vec![]))),
2434 });
2435 assert_eq!(expr.height(), 1);
2436 }
2437
2438 #[test]
2439 fn height_contains_any() {
2440 let expr = Expr::ExprNoExt(ExprNoExt::ContainsAny {
2441 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2442 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Set(vec![]))),
2443 });
2444 assert_eq!(expr.height(), 1);
2445 }
2446
2447 #[test]
2448 fn height_get_tag() {
2449 let expr = Expr::ExprNoExt(ExprNoExt::GetTag {
2450 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2451 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::String(
2452 "tag_name".into(),
2453 )))),
2454 });
2455 assert_eq!(expr.height(), 1);
2456 }
2457
2458 #[test]
2459 fn height_has_tag() {
2460 let expr = Expr::ExprNoExt(ExprNoExt::HasTag {
2461 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2462 right: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::String(
2463 "tag_name".into(),
2464 )))),
2465 });
2466 assert_eq!(expr.height(), 1);
2467 }
2468
2469 #[test]
2470 fn height_get_attr() {
2471 let expr = Expr::ExprNoExt(ExprNoExt::GetAttr {
2472 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2473 attr: "foo".into(),
2474 });
2475 assert_eq!(expr.height(), 1);
2476 }
2477
2478 #[test]
2479 fn height_has_attr() {
2480 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
2482 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2483 attr: "foo".into(),
2484 }));
2485 assert_eq!(expr.height(), 1);
2486
2487 use nonempty::nonempty;
2489 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2490 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2491 attr: nonempty!["user".into(), "profile".into()],
2492 }));
2493 assert_eq!(expr.height(), 1);
2494 }
2495
2496 #[test]
2497 fn height_like() {
2498 let expr = Expr::ExprNoExt(ExprNoExt::Like {
2499 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2500 pattern: vec![PatternElem::Wildcard],
2501 });
2502 assert_eq!(expr.height(), 1);
2503 }
2504
2505 #[test]
2506 fn height_is() {
2507 let expr = Expr::ExprNoExt(ExprNoExt::Is {
2509 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2510 entity_type: "User".into(),
2511 in_expr: None,
2512 });
2513 assert_eq!(expr.height(), 1);
2514
2515 let expr = Expr::ExprNoExt(ExprNoExt::Is {
2517 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2518 entity_type: "User".into(),
2519 in_expr: Some(Arc::new(Expr::ExprNoExt(ExprNoExt::Var(
2520 ast::Var::Resource,
2521 )))),
2522 });
2523 assert_eq!(expr.height(), 1);
2524
2525 let expr = Expr::ExprNoExt(ExprNoExt::Is {
2527 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2528 entity_type: "User".into(),
2529 in_expr: Some(Arc::new(Expr::ExprNoExt(ExprNoExt::Not {
2530 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Resource))),
2531 }))),
2532 });
2533 assert_eq!(expr.height(), 2);
2534 }
2535
2536 #[test]
2537 fn height_ext_func_call() {
2538 let expr = Expr::ExtFuncCall(ExtFuncCall {
2540 call: HashMap::from([(
2541 "ip".to_smolstr(),
2542 vec![Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::String(
2543 "127.0.0.1".into(),
2544 )))],
2545 )]),
2546 });
2547 assert_eq!(expr.height(), 1);
2548
2549 let expr = Expr::ExtFuncCall(ExtFuncCall {
2551 call: HashMap::from([(
2552 "ip".to_smolstr(),
2553 vec![Expr::ExprNoExt(ExprNoExt::Not {
2554 arg: Arc::new(Expr::ExprNoExt(ExprNoExt::Value(CedarValueJson::Bool(
2555 true,
2556 )))),
2557 })],
2558 )]),
2559 });
2560 assert_eq!(expr.height(), 2);
2561 }
2562}