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
967impl Expr {
968 pub fn try_into_expr<B>(self) -> Result<B::Expr, B::BuildError>
971 where
972 B: ExprBuilder,
973 B::BuildError: From<FromJsonError>,
974 {
975 let builder = B::new();
983 match self {
984 Expr::ExprNoExt(ExprNoExt::Value(jsonvalue)) => {
985 let ast_expr: ast::Expr = jsonvalue
987 .into_expr(&|| JsonDeserializationErrorContext::Policy {
988 id: ast::PolicyID::from_string(""),
989 })
990 .map(ast::Expr::from)
991 .map_err(|j| B::BuildError::from(j.into()))?;
992 ast_expr.try_into_expr::<B>()
993 }
994 Expr::ExprNoExt(ExprNoExt::Var(var)) => Ok(builder.var(var)),
995 Expr::ExprNoExt(ExprNoExt::Slot(slot)) => Ok(builder.slot(slot)),
996 Expr::ExprNoExt(ExprNoExt::Not { arg }) => {
997 Ok(builder.not(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
998 }
999 Expr::ExprNoExt(ExprNoExt::Neg { arg }) => {
1000 Ok(builder.neg(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
1001 }
1002 Expr::ExprNoExt(ExprNoExt::Eq { left, right }) => Ok(builder.is_eq(
1003 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1004 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1005 )),
1006 Expr::ExprNoExt(ExprNoExt::NotEq { left, right }) => Ok(builder.noteq(
1007 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1008 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1009 )),
1010 Expr::ExprNoExt(ExprNoExt::In { left, right }) => Ok(builder.is_in(
1011 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1012 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1013 )),
1014 Expr::ExprNoExt(ExprNoExt::Less { left, right }) => Ok(builder.less(
1015 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1016 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1017 )),
1018 Expr::ExprNoExt(ExprNoExt::LessEq { left, right }) => Ok(builder.lesseq(
1019 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1020 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1021 )),
1022 Expr::ExprNoExt(ExprNoExt::Greater { left, right }) => Ok(builder.greater(
1023 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1024 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1025 )),
1026 Expr::ExprNoExt(ExprNoExt::GreaterEq { left, right }) => Ok(builder.greatereq(
1027 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1028 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1029 )),
1030 Expr::ExprNoExt(ExprNoExt::And { left, right }) => Ok(builder.and(
1031 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1032 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1033 )),
1034 Expr::ExprNoExt(ExprNoExt::Or { left, right }) => Ok(builder.or(
1035 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1036 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1037 )),
1038 Expr::ExprNoExt(ExprNoExt::Add { left, right }) => Ok(builder.add(
1039 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1040 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1041 )),
1042 Expr::ExprNoExt(ExprNoExt::Sub { left, right }) => Ok(builder.sub(
1043 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1044 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1045 )),
1046 Expr::ExprNoExt(ExprNoExt::Mul { left, right }) => Ok(builder.mul(
1047 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1048 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1049 )),
1050 Expr::ExprNoExt(ExprNoExt::Contains { left, right }) => Ok(builder.contains(
1051 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1052 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1053 )),
1054 Expr::ExprNoExt(ExprNoExt::ContainsAll { left, right }) => Ok(builder.contains_all(
1055 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1056 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1057 )),
1058 Expr::ExprNoExt(ExprNoExt::ContainsAny { left, right }) => Ok(builder.contains_any(
1059 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1060 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1061 )),
1062 Expr::ExprNoExt(ExprNoExt::IsEmpty { arg }) => {
1063 Ok(builder.is_empty(Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
1064 }
1065 Expr::ExprNoExt(ExprNoExt::GetTag { left, right }) => Ok(builder.get_tag(
1066 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1067 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1068 )),
1069 Expr::ExprNoExt(ExprNoExt::HasTag { left, right }) => Ok(builder.has_tag(
1070 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1071 Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
1072 )),
1073 Expr::ExprNoExt(ExprNoExt::GetAttr { left, attr }) => {
1074 Ok(builder.get_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr))
1075 }
1076 Expr::ExprNoExt(ExprNoExt::HasAttr(repr)) => match repr {
1077 HasAttrRepr::Simple { left, attr } => {
1078 Ok(builder.has_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr))
1079 }
1080 HasAttrRepr::Extended { left, attr } => Ok(builder
1081 .extended_has_attr(Arc::unwrap_or_clone(left).try_into_expr::<B>()?, attr)),
1082 },
1083 Expr::ExprNoExt(ExprNoExt::Like { left, pattern }) => Ok(builder.like(
1084 Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
1085 ast::Pattern::from(pattern.as_slice()),
1086 )),
1087 Expr::ExprNoExt(ExprNoExt::Is {
1088 left,
1089 entity_type,
1090 in_expr,
1091 }) => {
1092 let entity_type_name = ast::EntityType::from_normalized_str(&entity_type)
1093 .map_err(FromJsonError::InvalidEntityType)?;
1094 let left_expr = Arc::unwrap_or_clone(left).try_into_expr::<B>()?;
1095 match in_expr {
1096 Some(in_expr) => Ok(builder.is_in_entity_type(
1097 left_expr,
1098 entity_type_name,
1099 Arc::unwrap_or_clone(in_expr).try_into_expr::<B>()?,
1100 )),
1101 None => Ok(builder.is_entity_type(left_expr, entity_type_name)),
1102 }
1103 }
1104 Expr::ExprNoExt(ExprNoExt::If {
1105 cond_expr,
1106 then_expr,
1107 else_expr,
1108 }) => Ok(builder.ite(
1109 Arc::unwrap_or_clone(cond_expr).try_into_expr::<B>()?,
1110 Arc::unwrap_or_clone(then_expr).try_into_expr::<B>()?,
1111 Arc::unwrap_or_clone(else_expr).try_into_expr::<B>()?,
1112 )),
1113 Expr::ExprNoExt(ExprNoExt::Set(elements)) => Ok(builder.set(
1114 elements
1115 .into_iter()
1116 .map(|el| el.try_into_expr::<B>())
1117 .collect::<Result<Vec<_>, _>>()?,
1118 )),
1119 Expr::ExprNoExt(ExprNoExt::Record(map)) =>
1120 {
1121 #[expect(
1122 clippy::expect_used,
1123 reason = "can't have duplicate keys here because the input was already a HashMap"
1124 )]
1125 Ok(builder
1126 .record(
1127 map.into_iter()
1128 .map(|(k, v)| Ok((k, v.try_into_expr::<B>()?)))
1129 .collect::<Result<Vec<_>, B::BuildError>>()?,
1130 )
1131 .expect("map should not have duplicate keys"))
1132 }
1133 Expr::ExtFuncCall(e) => {
1134 let (fn_name, args) = e.try_into_components()?;
1135 let fn_name = Name::from_normalized_str(&fn_name).map_err(|errs| {
1136 JsonDeserializationError::parse_escape(EscapeKind::Extension, fn_name, errs)
1137 .into()
1138 })?;
1139 builder.call_extension_fn(
1142 fn_name,
1143 args.into_iter()
1144 .map(|arg| arg.try_into_expr::<B>())
1145 .collect::<Result<Vec<_>, _>>()?,
1146 )
1147 }
1148 #[cfg(feature = "tolerant-ast")]
1149 Expr::ExprNoExt(ExprNoExt::Error(_)) => Err(FromJsonError::ASTErrorNode.into()),
1150 }
1151 }
1152
1153 pub fn try_into_ast(self, id: &ast::PolicyID) -> Result<ast::Expr, FromJsonError> {
1157 match self {
1158 Expr::ExprNoExt(ExprNoExt::Value(jsonvalue)) => jsonvalue
1159 .into_expr(&|| JsonDeserializationErrorContext::Policy { id: id.clone() })
1160 .map(Into::into)
1161 .map_err(Into::into),
1162 Expr::ExprNoExt(ExprNoExt::Var(var)) => Ok(ast::Expr::var(var)),
1163 Expr::ExprNoExt(ExprNoExt::Slot(slot)) => Ok(ast::Expr::slot(slot)),
1164 Expr::ExprNoExt(ExprNoExt::Not { arg }) => {
1165 Ok(ast::Expr::not(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
1166 }
1167 Expr::ExprNoExt(ExprNoExt::Neg { arg }) => {
1168 Ok(ast::Expr::neg(Arc::unwrap_or_clone(arg).try_into_ast(id)?))
1169 }
1170 Expr::ExprNoExt(ExprNoExt::Eq { left, right }) => Ok(ast::Expr::is_eq(
1171 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1172 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1173 )),
1174 Expr::ExprNoExt(ExprNoExt::NotEq { left, right }) => Ok(ast::Expr::noteq(
1175 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1176 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1177 )),
1178 Expr::ExprNoExt(ExprNoExt::In { left, right }) => Ok(ast::Expr::is_in(
1179 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1180 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1181 )),
1182 Expr::ExprNoExt(ExprNoExt::Less { left, right }) => Ok(ast::Expr::less(
1183 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1184 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1185 )),
1186 Expr::ExprNoExt(ExprNoExt::LessEq { left, right }) => Ok(ast::Expr::lesseq(
1187 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1188 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1189 )),
1190 Expr::ExprNoExt(ExprNoExt::Greater { left, right }) => Ok(ast::Expr::greater(
1191 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1192 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1193 )),
1194 Expr::ExprNoExt(ExprNoExt::GreaterEq { left, right }) => Ok(ast::Expr::greatereq(
1195 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1196 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1197 )),
1198 Expr::ExprNoExt(ExprNoExt::And { left, right }) => Ok(ast::Expr::and(
1199 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1200 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1201 )),
1202 Expr::ExprNoExt(ExprNoExt::Or { left, right }) => Ok(ast::Expr::or(
1203 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1204 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1205 )),
1206 Expr::ExprNoExt(ExprNoExt::Add { left, right }) => Ok(ast::Expr::add(
1207 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1208 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1209 )),
1210 Expr::ExprNoExt(ExprNoExt::Sub { left, right }) => Ok(ast::Expr::sub(
1211 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1212 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1213 )),
1214 Expr::ExprNoExt(ExprNoExt::Mul { left, right }) => Ok(ast::Expr::mul(
1215 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1216 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1217 )),
1218 Expr::ExprNoExt(ExprNoExt::Contains { left, right }) => Ok(ast::Expr::contains(
1219 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1220 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1221 )),
1222 Expr::ExprNoExt(ExprNoExt::ContainsAll { left, right }) => Ok(ast::Expr::contains_all(
1223 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1224 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1225 )),
1226 Expr::ExprNoExt(ExprNoExt::ContainsAny { left, right }) => Ok(ast::Expr::contains_any(
1227 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1228 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1229 )),
1230 Expr::ExprNoExt(ExprNoExt::IsEmpty { arg }) => Ok(ast::Expr::is_empty(
1231 Arc::unwrap_or_clone(arg).try_into_ast(id)?,
1232 )),
1233 Expr::ExprNoExt(ExprNoExt::GetTag { left, right }) => Ok(ast::Expr::get_tag(
1234 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1235 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1236 )),
1237 Expr::ExprNoExt(ExprNoExt::HasTag { left, right }) => Ok(ast::Expr::has_tag(
1238 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1239 Arc::unwrap_or_clone(right).try_into_ast(id)?,
1240 )),
1241 Expr::ExprNoExt(ExprNoExt::GetAttr { left, attr }) => Ok(ast::Expr::get_attr(
1242 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1243 attr,
1244 )),
1245 Expr::ExprNoExt(ExprNoExt::HasAttr(repr)) => match repr {
1246 HasAttrRepr::Simple { left, attr } => Ok(ast::Expr::has_attr(
1247 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1248 attr,
1249 )),
1250 HasAttrRepr::Extended { left, attr } => Ok(ast::ExprBuilder::new()
1251 .extended_has_attr(Arc::unwrap_or_clone(left).try_into_ast(id)?, attr)),
1252 },
1253 Expr::ExprNoExt(ExprNoExt::Like { left, pattern }) => Ok(ast::Expr::like(
1254 Arc::unwrap_or_clone(left).try_into_ast(id)?,
1255 crate::ast::Pattern::from(pattern.as_slice()),
1256 )),
1257 Expr::ExprNoExt(ExprNoExt::Is {
1258 left,
1259 entity_type,
1260 in_expr,
1261 }) => ast::EntityType::from_normalized_str(entity_type.as_str())
1262 .map_err(FromJsonError::InvalidEntityType)
1263 .and_then(|entity_type_name| {
1264 let left: ast::Expr = Arc::unwrap_or_clone(left).try_into_ast(id)?;
1265 match in_expr {
1266 Some(in_expr) => Ok(ast::ExprBuilder::new().is_in_entity_type(
1267 left,
1268 entity_type_name,
1269 Arc::unwrap_or_clone(in_expr).try_into_ast(id)?,
1270 )),
1271 None => Ok(ast::ExprBuilder::new().is_entity_type(left, entity_type_name)),
1272 }
1273 }),
1274 Expr::ExprNoExt(ExprNoExt::If {
1275 cond_expr,
1276 then_expr,
1277 else_expr,
1278 }) => Ok(ast::Expr::ite(
1279 Arc::unwrap_or_clone(cond_expr).try_into_ast(id)?,
1280 Arc::unwrap_or_clone(then_expr).try_into_ast(id)?,
1281 Arc::unwrap_or_clone(else_expr).try_into_ast(id)?,
1282 )),
1283 Expr::ExprNoExt(ExprNoExt::Set(elements)) => Ok(ast::Expr::set(
1284 elements
1285 .into_iter()
1286 .map(|el| el.try_into_ast(id))
1287 .collect::<Result<Vec<_>, FromJsonError>>()?,
1288 )),
1289 Expr::ExprNoExt(ExprNoExt::Record(map)) =>
1290 {
1291 #[expect(
1292 clippy::expect_used,
1293 reason = "can't have duplicate keys here because the input was already a HashMap"
1294 )]
1295 Ok(ast::Expr::record(
1296 map.into_iter()
1297 .map(|(k, v)| Ok((k, v.try_into_ast(id)?)))
1298 .collect::<Result<HashMap<SmolStr, _>, FromJsonError>>()?,
1299 )
1300 .expect("can't have duplicate keys here because the input was already a HashMap"))
1301 }
1302 Expr::ExtFuncCall(e) => {
1303 let (fn_name, args) = e.try_into_components()?;
1304 let fn_name = Name::from_normalized_str(&fn_name).map_err(|errs| {
1305 JsonDeserializationError::parse_escape(EscapeKind::Extension, fn_name, errs)
1306 })?;
1307 if ExtStyles::is_known_extension_func_name(&fn_name) {
1308 Ok(ast::Expr::call_extension_fn(
1309 fn_name,
1310 args.into_iter()
1311 .map(|arg| arg.try_into_ast(id))
1312 .collect::<Result<_, _>>()?,
1313 ))
1314 } else {
1315 Err(FromJsonError::UnknownExtensionFunction(fn_name))
1316 }
1317 }
1318 #[cfg(feature = "tolerant-ast")]
1319 Expr::ExprNoExt(ExprNoExt::Error(_)) => Err(FromJsonError::ASTErrorNode),
1320 }
1321 }
1322}
1323
1324impl From<ast::Literal> for Expr {
1325 fn from(lit: ast::Literal) -> Expr {
1326 Builder::new().val(lit)
1327 }
1328}
1329
1330impl From<ast::Var> for Expr {
1331 fn from(var: ast::Var) -> Expr {
1332 Builder::new().var(var)
1333 }
1334}
1335
1336impl From<ast::SlotId> for Expr {
1337 fn from(slot: ast::SlotId) -> Expr {
1338 Builder::new().slot(slot)
1339 }
1340}
1341
1342impl TryFrom<&Node<Option<cst::Expr>>> for Expr {
1343 type Error = ParseErrors;
1344 fn try_from(e: &Node<Option<cst::Expr>>) -> Result<Expr, ParseErrors> {
1345 e.to_expr::<Builder>()
1346 }
1347}
1348
1349impl std::fmt::Display for Expr {
1350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1351 match self {
1352 Self::ExprNoExt(e) => write!(f, "{e}"),
1353 Self::ExtFuncCall(e) => write!(f, "{e}"),
1354 }
1355 }
1356}
1357
1358impl BoundedDisplay for Expr {
1359 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1360 match self {
1361 Self::ExprNoExt(e) => BoundedDisplay::fmt(e, f, n),
1362 Self::ExtFuncCall(e) => BoundedDisplay::fmt(e, f, n),
1363 }
1364 }
1365}
1366
1367fn display_cedarvaluejson(
1368 f: &mut impl std::fmt::Write,
1369 v: &CedarValueJson,
1370 n: Option<usize>,
1371) -> std::fmt::Result {
1372 match v {
1373 CedarValueJson::Long(i) if *i < 0 => write!(f, "({i})"),
1376 CedarValueJson::Long(i) => write!(f, "{i}"),
1377 CedarValueJson::Bool(b) => write!(f, "{b}"),
1378 CedarValueJson::String(s) => write!(f, "\"{}\"", s.escape_debug()),
1379 CedarValueJson::EntityEscape { __entity } => {
1380 match ast::EntityUID::try_from(__entity.clone()) {
1381 Ok(euid) => write!(f, "{euid}"),
1382 Err(e) => write!(f, "(invalid entity uid: {e})"),
1383 }
1384 }
1385 CedarValueJson::ExprEscape { __expr } => write!(f, "({__expr})"),
1386 CedarValueJson::ExtnEscape {
1387 __extn: FnAndArgs::Single { ext_fn, arg },
1388 } => {
1389 let style = Extensions::all_available().all_funcs().find_map(|f| {
1391 if &f.name().to_smolstr() == ext_fn {
1392 Some(f.style())
1393 } else {
1394 None
1395 }
1396 });
1397 match style {
1398 Some(ast::CallStyle::MethodStyle) => {
1399 display_cedarvaluejson(f, arg, n)?;
1400 write!(f, ".{ext_fn}()")?;
1401 Ok(())
1402 }
1403 Some(ast::CallStyle::FunctionStyle) | None => {
1404 write!(f, "{ext_fn}(")?;
1405 display_cedarvaluejson(f, arg, n)?;
1406 write!(f, ")")?;
1407 Ok(())
1408 }
1409 }
1410 }
1411 CedarValueJson::ExtnEscape {
1412 __extn: FnAndArgs::Multi { ext_fn, args },
1413 } => {
1414 let style = Extensions::all_available().all_funcs().find_map(|f| {
1416 if &f.name().to_smolstr() == ext_fn {
1417 Some(f.style())
1418 } else {
1419 None
1420 }
1421 });
1422 match style {
1423 Some(ast::CallStyle::MethodStyle) => {
1424 #[expect(
1425 clippy::indexing_slicing,
1426 reason = "method-style calls must have more than one argument"
1427 )]
1428 display_cedarvaluejson(f, &args[0], n)?;
1429 write!(f, ".{ext_fn}(")?;
1430 #[expect(
1431 clippy::indexing_slicing,
1432 reason = "method-style calls must have more than one argument"
1433 )]
1434 match &args[1..] {
1435 [] => {}
1436 [args @ .., last] => {
1437 for arg in args {
1438 display_cedarvaluejson(f, arg, n)?;
1439 write!(f, ", ")?;
1440 }
1441 display_cedarvaluejson(f, last, n)?;
1442 }
1443 }
1444 write!(f, ")")?;
1445 Ok(())
1446 }
1447 Some(ast::CallStyle::FunctionStyle) | None => {
1448 write!(f, "{ext_fn}(")?;
1449 match &args[..] {
1450 [] => {}
1451 [args @ .., last] => {
1452 for arg in args {
1453 display_cedarvaluejson(f, arg, n)?;
1454 write!(f, ", ")?;
1455 }
1456 display_cedarvaluejson(f, last, n)?;
1457 }
1458 }
1459 write!(f, ")")?;
1460 Ok(())
1461 }
1462 }
1463 }
1464 CedarValueJson::Set(v) => {
1465 match n {
1466 Some(n) if v.len() > n => {
1467 write!(f, "[")?;
1469 for val in v.iter().take(n) {
1470 display_cedarvaluejson(f, val, Some(n))?;
1471 write!(f, ", ")?;
1472 }
1473 write!(f, "..]")?;
1474 Ok(())
1475 }
1476 _ => {
1477 write!(f, "[")?;
1479 for (i, val) in v.iter().enumerate() {
1480 display_cedarvaluejson(f, val, n)?;
1481 if i < v.len() - 1 {
1482 write!(f, ", ")?;
1483 }
1484 }
1485 write!(f, "]")?;
1486 Ok(())
1487 }
1488 }
1489 }
1490 CedarValueJson::Record(r) => {
1491 match n {
1492 Some(n) if r.len() > n => {
1493 write!(f, "{{")?;
1495 for (k, v) in r.iter().take(n) {
1496 write!(f, "\"{}\": ", k.escape_debug())?;
1497 display_cedarvaluejson(f, v, Some(n))?;
1498 write!(f, ", ")?;
1499 }
1500 write!(f, "..}}")?;
1501 Ok(())
1502 }
1503 _ => {
1504 write!(f, "{{")?;
1506 for (i, (k, v)) in r.iter().enumerate() {
1507 write!(f, "\"{}\": ", k.escape_debug())?;
1508 display_cedarvaluejson(f, v, n)?;
1509 if i < r.len() - 1 {
1510 write!(f, ", ")?;
1511 }
1512 }
1513 write!(f, "}}")?;
1514 Ok(())
1515 }
1516 }
1517 }
1518 CedarValueJson::Null => {
1519 write!(f, "null")?;
1520 Ok(())
1521 }
1522 }
1523}
1524
1525impl std::fmt::Display for ExprNoExt {
1526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1527 BoundedDisplay::fmt_unbounded(self, f)
1528 }
1529}
1530
1531impl BoundedDisplay for ExprNoExt {
1532 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1533 match &self {
1534 ExprNoExt::Value(v) => display_cedarvaluejson(f, v, n),
1535 ExprNoExt::Var(v) => write!(f, "{v}"),
1536 ExprNoExt::Slot(id) => write!(f, "{id}"),
1537 ExprNoExt::Not { arg } => {
1538 write!(f, "!")?;
1539 maybe_with_parens(f, arg, n)
1540 }
1541 ExprNoExt::Neg { arg } => {
1542 write!(f, "-({arg})")
1548 }
1549 ExprNoExt::Eq { left, right } => {
1550 maybe_with_parens(f, left, n)?;
1551 write!(f, " == ")?;
1552 maybe_with_parens(f, right, n)
1553 }
1554 ExprNoExt::NotEq { left, right } => {
1555 maybe_with_parens(f, left, n)?;
1556 write!(f, " != ")?;
1557 maybe_with_parens(f, right, n)
1558 }
1559 ExprNoExt::In { left, right } => {
1560 maybe_with_parens(f, left, n)?;
1561 write!(f, " in ")?;
1562 maybe_with_parens(f, right, n)
1563 }
1564 ExprNoExt::Less { left, right } => {
1565 maybe_with_parens(f, left, n)?;
1566 write!(f, " < ")?;
1567 maybe_with_parens(f, right, n)
1568 }
1569 ExprNoExt::LessEq { left, right } => {
1570 maybe_with_parens(f, left, n)?;
1571 write!(f, " <= ")?;
1572 maybe_with_parens(f, right, n)
1573 }
1574 ExprNoExt::Greater { left, right } => {
1575 maybe_with_parens(f, left, n)?;
1576 write!(f, " > ")?;
1577 maybe_with_parens(f, right, n)
1578 }
1579 ExprNoExt::GreaterEq { left, right } => {
1580 maybe_with_parens(f, left, n)?;
1581 write!(f, " >= ")?;
1582 maybe_with_parens(f, right, n)
1583 }
1584 ExprNoExt::And { left, right } => {
1585 maybe_with_parens(f, left, n)?;
1586 write!(f, " && ")?;
1587 maybe_with_parens(f, right, n)
1588 }
1589 ExprNoExt::Or { left, right } => {
1590 maybe_with_parens(f, left, n)?;
1591 write!(f, " || ")?;
1592 maybe_with_parens(f, right, n)
1593 }
1594 ExprNoExt::Add { left, right } => {
1595 maybe_with_parens(f, left, n)?;
1596 write!(f, " + ")?;
1597 maybe_with_parens(f, right, n)
1598 }
1599 ExprNoExt::Sub { left, right } => {
1600 maybe_with_parens(f, left, n)?;
1601 write!(f, " - ")?;
1602 maybe_with_parens(f, right, n)
1603 }
1604 ExprNoExt::Mul { left, right } => {
1605 maybe_with_parens(f, left, n)?;
1606 write!(f, " * ")?;
1607 maybe_with_parens(f, right, n)
1608 }
1609 ExprNoExt::Contains { left, right } => {
1610 maybe_with_parens(f, left, n)?;
1611 write!(f, ".contains({right})")
1612 }
1613 ExprNoExt::ContainsAll { left, right } => {
1614 maybe_with_parens(f, left, n)?;
1615 write!(f, ".containsAll({right})")
1616 }
1617 ExprNoExt::ContainsAny { left, right } => {
1618 maybe_with_parens(f, left, n)?;
1619 write!(f, ".containsAny({right})")
1620 }
1621 ExprNoExt::IsEmpty { arg } => {
1622 maybe_with_parens(f, arg, n)?;
1623 write!(f, ".isEmpty()")
1624 }
1625 ExprNoExt::GetTag { left, right } => {
1626 maybe_with_parens(f, left, n)?;
1627 write!(f, ".getTag({right})")
1628 }
1629 ExprNoExt::HasTag { left, right } => {
1630 maybe_with_parens(f, left, n)?;
1631 write!(f, ".hasTag({right})")
1632 }
1633 ExprNoExt::GetAttr { left, attr } => {
1634 maybe_with_parens(f, left, n)?;
1635 if is_normalized_ident(attr) {
1636 write!(f, ".{}", attr)
1637 } else {
1638 write!(f, "[\"{}\"]", attr.escape_debug())
1639 }
1640 }
1641 ExprNoExt::HasAttr(repr) => match repr {
1642 HasAttrRepr::Simple { left, attr } => {
1643 maybe_with_parens(f, left, n)?;
1644 if is_normalized_ident(attr) {
1645 write!(f, " has {}", attr)
1646 } else {
1647 write!(f, " has \"{}\"", attr.escape_debug())
1648 }
1649 }
1650 HasAttrRepr::Extended { left, attr } => {
1651 maybe_with_parens(f, left, n)?;
1652 if is_normalized_ident(&attr.head) {
1653 write!(f, " has {}", attr.head)?;
1654 } else {
1655 write!(f, " has \"{}\"", attr.head.escape_debug())?;
1656 }
1657 for attr in attr.tail.iter() {
1658 if is_normalized_ident(attr) {
1661 write!(f, ".{}", attr)?;
1662 } else {
1663 write!(f, ".\"{}\"", attr.escape_debug())?;
1664 }
1665 }
1666 Ok(())
1667 }
1668 },
1669 ExprNoExt::Like { left, pattern } => {
1670 maybe_with_parens(f, left, n)?;
1671 write!(
1672 f,
1673 " like \"{}\"",
1674 crate::ast::Pattern::from(pattern.as_slice())
1675 )
1676 }
1677 ExprNoExt::Is {
1678 left,
1679 entity_type,
1680 in_expr,
1681 } => {
1682 maybe_with_parens(f, left, n)?;
1683 write!(f, " is {entity_type}")?;
1684 match in_expr {
1685 Some(in_expr) => {
1686 write!(f, " in ")?;
1687 maybe_with_parens(f, in_expr, n)
1688 }
1689 None => Ok(()),
1690 }
1691 }
1692 ExprNoExt::If {
1693 cond_expr,
1694 then_expr,
1695 else_expr,
1696 } => {
1697 write!(f, "if ")?;
1698 maybe_with_parens(f, cond_expr, n)?;
1699 write!(f, " then ")?;
1700 maybe_with_parens(f, then_expr, n)?;
1701 write!(f, " else ")?;
1702 maybe_with_parens(f, else_expr, n)
1703 }
1704 ExprNoExt::Set(v) => {
1705 match n {
1706 Some(n) if v.len() > n => {
1707 write!(f, "[")?;
1709 for element in v.iter().take(n) {
1710 BoundedDisplay::fmt(element, f, Some(n))?;
1711 write!(f, ", ")?;
1712 }
1713 write!(f, "..]")?;
1714 Ok(())
1715 }
1716 _ => {
1717 write!(f, "[")?;
1719 for (i, element) in v.iter().enumerate() {
1720 BoundedDisplay::fmt(element, f, n)?;
1721 if i < v.len() - 1 {
1722 write!(f, ", ")?;
1723 }
1724 }
1725 write!(f, "]")?;
1726 Ok(())
1727 }
1728 }
1729 }
1730 ExprNoExt::Record(m) => {
1731 match n {
1732 Some(n) if m.len() > n => {
1733 write!(f, "{{")?;
1735 for (k, v) in m.iter().take(n) {
1736 if is_normalized_ident(k) {
1737 write!(f, "{k}: ")?;
1738 } else {
1739 write!(f, "\"{}\": ", k.escape_debug())?;
1740 }
1741 BoundedDisplay::fmt(v, f, Some(n))?;
1742 write!(f, ", ")?;
1743 }
1744 write!(f, "..}}")?;
1745 Ok(())
1746 }
1747 _ => {
1748 write!(f, "{{")?;
1750 for (i, (k, v)) in m.iter().enumerate() {
1751 if is_normalized_ident(k) {
1752 write!(f, "{k}: ")?;
1753 } else {
1754 write!(f, "\"{}\": ", k.escape_debug())?;
1755 }
1756 BoundedDisplay::fmt(v, f, n)?;
1757 if i < m.len() - 1 {
1758 write!(f, ", ")?;
1759 }
1760 }
1761 write!(f, "}}")?;
1762 Ok(())
1763 }
1764 }
1765 }
1766 #[cfg(feature = "tolerant-ast")]
1767 ExprNoExt::Error(e) => {
1768 write!(f, "{e}")?;
1769 Ok(())
1770 }
1771 }
1772 }
1773}
1774
1775impl std::fmt::Display for ExtFuncCall {
1776 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1777 BoundedDisplay::fmt_unbounded(self, f)
1778 }
1779}
1780
1781impl BoundedDisplay for ExtFuncCall {
1782 fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1783 #[expect(clippy::unwrap_used, reason = "safe due to INVARIANT on `ExtFuncCall`")]
1784 let (fn_name, args) = self.try_components().unwrap();
1785 let style = Extensions::all_available().all_funcs().find_map(|ext_fn| {
1787 if &ext_fn.name().to_smolstr() == fn_name {
1788 Some(ext_fn.style())
1789 } else {
1790 None
1791 }
1792 });
1793 match (style, args) {
1794 (Some(ast::CallStyle::MethodStyle), [receiver, rest @ ..]) => {
1795 maybe_with_parens(f, receiver, n)?;
1796 write!(f, ".{}({})", fn_name, rest.iter().join(", "))
1797 }
1798 (_, _) => {
1799 write!(f, "{}({})", fn_name, args.iter().join(", "))
1800 }
1801 }
1802 }
1803}
1804
1805fn maybe_with_parens(
1811 f: &mut impl std::fmt::Write,
1812 expr: &Expr,
1813 n: Option<usize>,
1814) -> std::fmt::Result {
1815 match expr {
1816 Expr::ExprNoExt(ExprNoExt::Set(_)) |
1817 Expr::ExprNoExt(ExprNoExt::Record(_)) |
1818 Expr::ExprNoExt(ExprNoExt::Value(_)) |
1819 Expr::ExprNoExt(ExprNoExt::Var(_)) |
1820 Expr::ExprNoExt(ExprNoExt::Slot(_)) => BoundedDisplay::fmt(expr, f, n),
1821
1822 Expr::ExprNoExt(ExprNoExt::Not { .. }) |
1825 Expr::ExprNoExt(ExprNoExt::Neg { .. }) |
1828 Expr::ExprNoExt(ExprNoExt::Eq { .. }) |
1829 Expr::ExprNoExt(ExprNoExt::NotEq { .. }) |
1830 Expr::ExprNoExt(ExprNoExt::In { .. }) |
1831 Expr::ExprNoExt(ExprNoExt::Less { .. }) |
1832 Expr::ExprNoExt(ExprNoExt::LessEq { .. }) |
1833 Expr::ExprNoExt(ExprNoExt::Greater { .. }) |
1834 Expr::ExprNoExt(ExprNoExt::GreaterEq { .. }) |
1835 Expr::ExprNoExt(ExprNoExt::And { .. }) |
1836 Expr::ExprNoExt(ExprNoExt::Or { .. }) |
1837 Expr::ExprNoExt(ExprNoExt::Add { .. }) |
1838 Expr::ExprNoExt(ExprNoExt::Sub { .. }) |
1839 Expr::ExprNoExt(ExprNoExt::Mul { .. }) |
1840 Expr::ExprNoExt(ExprNoExt::Contains { .. }) |
1841 Expr::ExprNoExt(ExprNoExt::ContainsAll { .. }) |
1842 Expr::ExprNoExt(ExprNoExt::ContainsAny { .. }) |
1843 Expr::ExprNoExt(ExprNoExt::IsEmpty { .. }) |
1844 Expr::ExprNoExt(ExprNoExt::GetAttr { .. }) |
1845 Expr::ExprNoExt(ExprNoExt::HasAttr { .. }) |
1846 Expr::ExprNoExt(ExprNoExt::GetTag { .. }) |
1847 Expr::ExprNoExt(ExprNoExt::HasTag { .. }) |
1848 Expr::ExprNoExt(ExprNoExt::Like { .. }) |
1849 Expr::ExprNoExt(ExprNoExt::Is { .. }) |
1850 Expr::ExprNoExt(ExprNoExt::If { .. }) |
1851 Expr::ExtFuncCall { .. } => {
1852 write!(f, "(")?;
1853 BoundedDisplay::fmt(expr, f, n)?;
1854 write!(f, ")")?;
1855 Ok(())
1856 },
1857 #[cfg(feature = "tolerant-ast")]
1858 Expr::ExprNoExt(ExprNoExt::Error { .. }) => {
1859 write!(f, "(")?;
1860 BoundedDisplay::fmt(expr, f, n)?;
1861 write!(f, ")")?;
1862 Ok(())
1863 }
1864 }
1865}
1866
1867#[cfg(test)]
1868#[expect(clippy::indexing_slicing, reason = "this is unit test code")]
1869#[expect(clippy::panic, reason = "Unit Test Code")]
1870mod test {
1871 use crate::parser::{
1872 err::{ParseError, ToASTErrorKind},
1873 parse_expr,
1874 };
1875
1876 use super::*;
1877 use ast::BoundedToString;
1878 use cool_asserts::assert_matches;
1879
1880 #[test]
1881 fn test_invalid_expr_from_cst_name() {
1882 let e = crate::parser::text_to_cst::parse_expr("some_long_str::else").unwrap();
1883 assert_matches!(Expr::try_from(&e), Err(e) => {
1884 assert!(e.len() == 1);
1885 assert_matches!(&e[0],
1886 ParseError::ToAST(to_ast_error) => {
1887 assert_matches!(to_ast_error.kind(), ToASTErrorKind::ReservedIdentifier(s) => {
1888 assert_eq!(s.to_string(), "else");
1889 });
1890 }
1891 );
1892 });
1893 }
1894
1895 #[test]
1896 fn display_and_bounded_display() {
1897 let expr = parse_expr(r#"[100, [3, 4, 5], -20, "foo"]"#)
1898 .unwrap()
1899 .into_expr::<Builder>();
1900 assert_eq!(format!("{expr}"), r#"[100, [3, 4, 5], (-20), "foo"]"#);
1901 assert_eq!(
1902 BoundedToString::to_string(&expr, None),
1903 r#"[100, [3, 4, 5], (-20), "foo"]"#
1904 );
1905 assert_eq!(
1906 BoundedToString::to_string(&expr, Some(4)),
1907 r#"[100, [3, 4, 5], (-20), "foo"]"#
1908 );
1909 assert_eq!(
1910 BoundedToString::to_string(&expr, Some(3)),
1911 r#"[100, [3, 4, 5], (-20), ..]"#
1912 );
1913 assert_eq!(
1914 BoundedToString::to_string(&expr, Some(2)),
1915 r#"[100, [3, 4, ..], ..]"#
1916 );
1917 assert_eq!(BoundedToString::to_string(&expr, Some(1)), r#"[100, ..]"#);
1918 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"[..]"#);
1919
1920 let expr = parse_expr(
1921 r#"{
1922 a: 12,
1923 b: [3, 4, true],
1924 c: -20,
1925 "hello ∞ world": "∂µß≈¥"
1926 }"#,
1927 )
1928 .unwrap()
1929 .into_expr::<Builder>();
1930 assert_eq!(
1931 format!("{expr}"),
1932 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
1933 );
1934 assert_eq!(
1935 BoundedToString::to_string(&expr, None),
1936 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
1937 );
1938 assert_eq!(
1939 BoundedToString::to_string(&expr, Some(4)),
1940 r#"{a: 12, b: [3, 4, true], c: (-20), "hello ∞ world": "∂µß≈¥"}"#
1941 );
1942 assert_eq!(
1943 BoundedToString::to_string(&expr, Some(3)),
1944 r#"{a: 12, b: [3, 4, true], c: (-20), ..}"#
1945 );
1946 assert_eq!(
1947 BoundedToString::to_string(&expr, Some(2)),
1948 r#"{a: 12, b: [3, 4, ..], ..}"#
1949 );
1950 assert_eq!(BoundedToString::to_string(&expr, Some(1)), r#"{a: 12, ..}"#);
1951 assert_eq!(BoundedToString::to_string(&expr, Some(0)), r#"{..}"#);
1952 }
1953
1954 #[test]
1955 fn display_get_attr() {
1956 let expr = parse_expr(r#"context.foo"#).unwrap().into_expr::<Builder>();
1958 assert_eq!(format!("{expr}"), r#"context.foo"#);
1959
1960 let expr = parse_expr(r#"context["foo"]"#)
1961 .unwrap()
1962 .into_expr::<Builder>();
1963 assert_eq!(format!("{expr}"), r#"context.foo"#);
1964
1965 let expr = parse_expr(r#"context["foo "]"#)
1967 .unwrap()
1968 .into_expr::<Builder>();
1969 assert_eq!(format!("{expr}"), r#"context["foo "]"#);
1970
1971 let expr = parse_expr(r#"context["foo-baz"]"#)
1972 .unwrap()
1973 .into_expr::<Builder>();
1974 assert_eq!(format!("{expr}"), r#"context["foo-baz"]"#);
1975
1976 let expr = parse_expr(r#"context["true"]"#)
1977 .unwrap()
1978 .into_expr::<Builder>();
1979 assert_eq!(format!("{expr}"), r#"context["true"]"#);
1980
1981 let expr = parse_expr(r#"context has foo"#)
1983 .unwrap()
1984 .into_expr::<Builder>();
1985 assert_eq!(format!("{expr}"), r#"context has foo"#);
1986
1987 let expr = parse_expr(r#"context has "foo""#)
1988 .unwrap()
1989 .into_expr::<Builder>();
1990 assert_eq!(format!("{expr}"), r#"context has foo"#);
1991
1992 let expr = parse_expr(r#"context has "foo-baz""#)
1993 .unwrap()
1994 .into_expr::<Builder>();
1995 assert_eq!(format!("{expr}"), r#"context has "foo-baz""#);
1996
1997 let expr = parse_expr(r#"if context has "if" then false else true"#)
1998 .unwrap()
1999 .into_expr::<Builder>();
2000 assert_eq!(
2001 format!("{expr}"),
2002 r#"if (context has "if") then false else true"#
2003 );
2004
2005 let expr = parse_expr(r#"context has "has""#)
2006 .unwrap()
2007 .into_expr::<Builder>();
2008 assert_eq!(format!("{expr}"), r#"context has "has""#);
2009
2010 let expr = parse_expr(r#"if context has "foo-baz" then false else true"#)
2011 .unwrap()
2012 .into_expr::<Builder>();
2013 assert_eq!(
2014 format!("{expr}"),
2015 r#"if (context has "foo-baz") then false else true"#
2016 );
2017 }
2018
2019 #[test]
2020 fn has_attr_serde() {
2021 use nonempty::nonempty;
2022 let test_cases = vec![
2023 (
2025 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
2026 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2027 attr: "department".into(),
2028 })),
2029 serde_json::json!({"has": {"left": {"Var": "principal"}, "attr": "department"}}),
2030 ),
2031 (
2033 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2034 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2035 attr: nonempty!["user".into()],
2036 })),
2037 serde_json::json!({"has": {"left": {"Var": "context"}, "attr": ["user"]}}),
2038 ),
2039 (
2041 Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2042 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2043 attr: nonempty!["user".into(), "profile".into(), "email".into()],
2044 })),
2045 serde_json::json!({"has": {"left": {"Var": "context"}, "attr": ["user", "profile", "email"]}}),
2046 ),
2047 ];
2048 for (expr, json_repr) in test_cases {
2049 assert_eq!(serde_json::to_value(&expr).unwrap(), json_repr);
2051 assert_eq!(expr, serde_json::from_value(json_repr).unwrap());
2053 }
2054 }
2055
2056 #[test]
2057 fn deserialize_has_attr_empty_errors() {
2058 let json = serde_json::json!({"has": {"left": {"Var": "context"}, "attr": []}});
2059 let expr_result: Result<Expr, _> = serde_json::from_value(json);
2060 assert!(expr_result.is_err())
2061 }
2062
2063 #[test]
2064 fn extended_has_display() {
2065 use nonempty::nonempty;
2068
2069 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Simple {
2071 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Principal))),
2072 attr: "department".into(),
2073 }));
2074 assert_eq!(format!("{expr}"), "principal has department");
2075
2076 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2078 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2079 attr: nonempty!["user".into()],
2080 }));
2081 assert_eq!(format!("{expr}"), "context has user");
2082
2083 let expr = Expr::ExprNoExt(ExprNoExt::HasAttr(HasAttrRepr::Extended {
2085 left: Arc::new(Expr::ExprNoExt(ExprNoExt::Var(ast::Var::Context))),
2086 attr: nonempty!["user".into(), "profile".into(), "email".into()],
2087 }));
2088 assert_eq!(format!("{expr}"), "context has user.profile.email");
2089 }
2090}