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