cedar_policy_core/est/
expr.rs

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