Skip to main content

cedar_policy_core/ast/
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
17#[cfg(feature = "tolerant-ast")]
18use {
19    super::expr_allows_errors::AstExprErrorKind,
20    crate::parser::err::{ToASTError, ToASTErrorKind},
21};
22
23use crate::{
24    ast::*,
25    expr_builder::{self, ExprBuilder as _},
26    extensions::Extensions,
27    parser::{err::ParseErrors, Loc},
28};
29use educe::Educe;
30use miette::Diagnostic;
31use nonempty::NonEmpty;
32use serde::{Deserialize, Serialize};
33use smol_str::SmolStr;
34use std::{
35    borrow::Cow,
36    collections::{btree_map, BTreeMap, HashMap},
37    hash::{Hash, Hasher},
38    mem,
39    sync::Arc,
40};
41use thiserror::Error;
42
43#[cfg(feature = "wasm")]
44extern crate tsify;
45
46/// Internal AST for expressions used by the policy evaluator.
47/// This structure is a wrapper around an `ExprKind`, which is the expression
48/// variant this object contains. It also contains source information about
49/// where the expression was written in policy source code, and some generic
50/// data which is stored on each node of the AST.
51/// Cloning is O(1).
52#[derive(Educe, Debug, Clone)]
53#[educe(PartialEq, Eq, Hash)]
54pub struct Expr<T = ()> {
55    expr_kind: ExprKind<T>,
56    #[educe(PartialEq(ignore))]
57    #[educe(Hash(ignore))]
58    source_loc: Option<Loc>,
59    data: T,
60}
61
62/// The possible expression variants. This enum should be matched on by code
63/// recursively traversing the AST.
64#[derive(Hash, Debug, Clone, PartialEq, Eq)]
65pub enum ExprKind<T = ()> {
66    /// Literal value
67    Lit(Literal),
68    /// Variable
69    Var(Var),
70    /// Template Slots
71    Slot(SlotId),
72    /// Symbolic Unknown for partial-eval
73    Unknown(Unknown),
74    /// Ternary expression
75    If {
76        /// Condition for the ternary expression. Must evaluate to Bool type
77        test_expr: Arc<Expr<T>>,
78        /// Value if true
79        then_expr: Arc<Expr<T>>,
80        /// Value if false
81        else_expr: Arc<Expr<T>>,
82    },
83    /// Boolean AND
84    And {
85        /// Left operand, which will be eagerly evaluated
86        left: Arc<Expr<T>>,
87        /// Right operand, which may not be evaluated due to short-circuiting
88        right: Arc<Expr<T>>,
89    },
90    /// Boolean OR
91    Or {
92        /// Left operand, which will be eagerly evaluated
93        left: Arc<Expr<T>>,
94        /// Right operand, which may not be evaluated due to short-circuiting
95        right: Arc<Expr<T>>,
96    },
97    /// Application of a built-in unary operator (single parameter)
98    UnaryApp {
99        /// Unary operator to apply
100        op: UnaryOp,
101        /// Argument to apply operator to
102        arg: Arc<Expr<T>>,
103    },
104    /// Application of a built-in binary operator (two parameters)
105    BinaryApp {
106        /// Binary operator to apply
107        op: BinaryOp,
108        /// First arg
109        arg1: Arc<Expr<T>>,
110        /// Second arg
111        arg2: Arc<Expr<T>>,
112    },
113    /// Application of an extension function to n arguments
114    /// INVARIANT (MethodStyleArgs):
115    ///   if op.style is MethodStyle then args _cannot_ be empty.
116    ///     The first element of args refers to the subject of the method call
117    /// Ideally, we find some way to make this non-representable.
118    ExtensionFunctionApp {
119        /// Extension function to apply
120        fn_name: Name,
121        /// Args to apply the function to
122        args: Arc<Vec<Expr<T>>>,
123    },
124    /// Get an attribute of an entity, or a field of a record
125    GetAttr {
126        /// Expression to get an attribute/field of. Must evaluate to either
127        /// Entity or Record type
128        expr: Arc<Expr<T>>,
129        /// Attribute or field to get
130        attr: SmolStr,
131    },
132    /// Does the given `expr` have the given `attr`?
133    HasAttr {
134        /// Expression to test. Must evaluate to either Entity or Record type
135        expr: Arc<Expr<T>>,
136        /// Attribute or field to check for
137        attr: SmolStr,
138    },
139    /// Does the given `expr` have the given sequence of nested `attrs`?
140    // This form may merge with HasAttr once we have high confidence we're not introducing regressions
141    ExtHasAttr {
142        /// Expression to test. Must evaluate to either Entity or Record type
143        expr: Arc<Expr<T>>,
144        /// List of attribute to check for sequentially
145        attrs: NonEmpty<SmolStr>,
146    },
147    /// Regex-like string matching similar to IAM's `StringLike` operator.
148    Like {
149        /// Expression to test. Must evaluate to String type
150        expr: Arc<Expr<T>>,
151        /// Pattern to match on; can include the wildcard *, which matches any string.
152        /// To match a literal `*` in the test expression, users can use `\*`.
153        /// Be careful the backslash in `\*` must not be another escape sequence. For instance, `\\*` matches a backslash plus an arbitrary string.
154        pattern: Pattern,
155    },
156    /// Entity type test. Does the first argument have the entity type
157    /// specified by the second argument.
158    Is {
159        /// Expression to test. Must evaluate to an Entity.
160        expr: Arc<Expr<T>>,
161        /// The [`EntityType`] used for the type membership test.
162        entity_type: EntityType,
163    },
164    /// Set (whose elements may be arbitrary expressions)
165    //
166    // This is backed by `Vec` (and not e.g. `HashSet`), because two `Expr`s
167    // that are syntactically unequal, may actually be semantically equal --
168    // i.e., we can't do the dedup of duplicates until all of the `Expr`s are
169    // evaluated into `Value`s
170    Set(Arc<Vec<Expr<T>>>),
171    /// Anonymous record (whose elements may be arbitrary expressions)
172    Record(Arc<BTreeMap<SmolStr, Expr<T>>>),
173    #[cfg(feature = "tolerant-ast")]
174    /// Error expression - allows us to continue parsing even when we have errors
175    Error {
176        /// Type of error that led to the failure
177        error_kind: AstExprErrorKind,
178    },
179}
180
181impl<T> ExprKind<T> {
182    /// Get the variant order (same as derive(Ord) for enums)
183    fn variant_order(&self) -> u8 {
184        match self {
185            ExprKind::Lit(_) => 0,
186            ExprKind::Var(_) => 1,
187            ExprKind::Slot(_) => 2,
188            ExprKind::Unknown(_) => 3,
189            ExprKind::If { .. } => 4,
190            ExprKind::And { .. } => 5,
191            ExprKind::Or { .. } => 6,
192            ExprKind::UnaryApp { .. } => 7,
193            ExprKind::BinaryApp { .. } => 8,
194            ExprKind::ExtensionFunctionApp { .. } => 9,
195            ExprKind::GetAttr { .. } => 10,
196            ExprKind::HasAttr { .. } => 11,
197            ExprKind::ExtHasAttr { .. } => 12,
198            ExprKind::Like { .. } => 13,
199            ExprKind::Set(_) => 14,
200            ExprKind::Record(_) => 15,
201            ExprKind::Is { .. } => 16,
202            #[cfg(feature = "tolerant-ast")]
203            ExprKind::Error { .. } => 17,
204        }
205    }
206}
207
208impl From<Value> for Expr {
209    fn from(v: Value) -> Self {
210        Expr::from(v.value).with_maybe_source_loc(v.loc)
211    }
212}
213
214impl From<ValueKind> for Expr {
215    fn from(v: ValueKind) -> Self {
216        match v {
217            ValueKind::Lit(lit) => Expr::val(lit),
218            ValueKind::Set(set) => Expr::set(set.iter().map(|v| Expr::from(v.clone()))),
219            #[expect(
220                clippy::expect_used,
221                reason = "cannot have duplicate key because the input was already a BTreeMap"
222            )]
223            ValueKind::Record(record) => Expr::record(
224                Arc::unwrap_or_clone(record)
225                    .into_iter()
226                    .map(|(k, v)| (k, Expr::from(v))),
227            )
228            .expect("cannot have duplicate key because the input was already a BTreeMap"),
229            ValueKind::ExtensionValue(ev) => RestrictedExpr::from(ev.as_ref().clone()).into(),
230        }
231    }
232}
233
234impl From<PartialValue> for Expr {
235    fn from(pv: PartialValue) -> Self {
236        match pv {
237            PartialValue::Value(v) => Expr::from(v),
238            PartialValue::Residual(expr) => expr,
239        }
240    }
241}
242
243impl<T> Expr<T> {
244    pub(crate) fn new(expr_kind: ExprKind<T>, source_loc: Option<Loc>, data: T) -> Self {
245        Self {
246            expr_kind,
247            source_loc,
248            data,
249        }
250    }
251
252    /// Access the inner `ExprKind` for this `Expr`. The `ExprKind` is the
253    /// `enum` which specifies the expression variant, so it must be accessed by
254    /// any code matching and recursing on an expression.
255    pub fn expr_kind(&self) -> &ExprKind<T> {
256        &self.expr_kind
257    }
258
259    /// Access the inner `ExprKind`, taking ownership and consuming the `Expr`.
260    pub fn into_expr_kind(self) -> ExprKind<T> {
261        self.expr_kind
262    }
263
264    /// Access the data stored on the `Expr`.
265    pub fn data(&self) -> &T {
266        &self.data
267    }
268
269    /// Access the data stored on the `Expr`, taking ownership and consuming the
270    /// `Expr`.
271    pub fn into_data(self) -> T {
272        self.data
273    }
274
275    /// Consume the `Expr`, returning the `ExprKind`, `source_loc`, and stored
276    /// data.
277    pub fn into_parts(self) -> (ExprKind<T>, Option<Loc>, T) {
278        (self.expr_kind, self.source_loc, self.data)
279    }
280
281    /// Access the `Loc` stored on the `Expr`.
282    pub fn source_loc(&self) -> Option<&Loc> {
283        self.source_loc.as_ref()
284    }
285
286    /// Return the `Expr`, but with the new `source_loc` (or `None`).
287    pub fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
288        Self { source_loc, ..self }
289    }
290
291    /// Update the data for this `Expr`. A convenient function used by the
292    /// Validator in one place.
293    pub fn set_data(&mut self, data: T) {
294        self.data = data;
295    }
296
297    /// Check whether this expression is an entity reference
298    ///
299    /// This is used for policy scopes, where some syntax is
300    /// required to be an entity reference.
301    pub fn is_ref(&self) -> bool {
302        match &self.expr_kind {
303            ExprKind::Lit(lit) => lit.is_ref(),
304            _ => false,
305        }
306    }
307
308    /// Check whether this expression is a slot.
309    pub fn is_slot(&self) -> bool {
310        matches!(&self.expr_kind, ExprKind::Slot(_))
311    }
312
313    /// Check whether this expression is a set of entity references
314    ///
315    /// This is used for policy scopes, where some syntax is
316    /// required to be an entity reference set.
317    pub fn is_ref_set(&self) -> bool {
318        match &self.expr_kind {
319            ExprKind::Set(exprs) => exprs.iter().all(|e| e.is_ref()),
320            _ => false,
321        }
322    }
323
324    /// Iterate over all sub-expressions in this expression
325    pub fn subexpressions(&self) -> impl Iterator<Item = &Self> {
326        expr_iterator::ExprIterator::new(self)
327    }
328
329    /// Iterate over all of the slots in this policy AST
330    pub fn slots(&self) -> impl Iterator<Item = Slot> + '_ {
331        self.subexpressions()
332            .filter_map(|exp| match &exp.expr_kind {
333                ExprKind::Slot(slotid) => Some(Slot {
334                    id: *slotid,
335                    loc: exp.source_loc().cloned(),
336                }),
337                _ => None,
338            })
339    }
340
341    /// Determine if the expression is projectable under partial evaluation
342    /// An expression is projectable if it's guaranteed to never error on evaluation
343    /// This is true if the expression is entirely composed of values or unknowns
344    pub fn is_projectable(&self) -> bool {
345        self.subexpressions().all(|e| {
346            matches!(
347                e.expr_kind(),
348                ExprKind::Lit(_)
349                    | ExprKind::Unknown(_)
350                    | ExprKind::Set(_)
351                    | ExprKind::Var(_)
352                    | ExprKind::Record(_)
353            )
354        })
355    }
356
357    /// Try to compute the runtime type of this expression. This operation may
358    /// fail (returning `None`), for example, when asked to get the type of any
359    /// variables, any attributes of entities or records, or an `unknown`
360    /// without an explicitly annotated type.
361    ///
362    /// Also note that this is _not_ typechecking the expression. It does not
363    /// check that the expression actually evaluates to a value (as opposed to
364    /// erroring).
365    ///
366    /// Because of these limitations, this function should only be used to
367    /// obtain a type for use in diagnostics such as error strings.
368    pub fn try_type_of(&self, extensions: &Extensions<'_>) -> Option<Type> {
369        match &self.expr_kind {
370            ExprKind::Lit(l) => Some(l.type_of()),
371            ExprKind::Var(_) => None,
372            ExprKind::Slot(_) => None,
373            ExprKind::Unknown(u) => u.type_annotation.clone(),
374            ExprKind::If {
375                then_expr,
376                else_expr,
377                ..
378            } => {
379                let type_of_then = then_expr.try_type_of(extensions);
380                let type_of_else = else_expr.try_type_of(extensions);
381                if type_of_then == type_of_else {
382                    type_of_then
383                } else {
384                    None
385                }
386            }
387            ExprKind::And { .. } => Some(Type::Bool),
388            ExprKind::Or { .. } => Some(Type::Bool),
389            ExprKind::UnaryApp {
390                op: UnaryOp::Neg, ..
391            } => Some(Type::Long),
392            ExprKind::UnaryApp {
393                op: UnaryOp::Not, ..
394            } => Some(Type::Bool),
395            ExprKind::UnaryApp {
396                op: UnaryOp::IsEmpty,
397                ..
398            } => Some(Type::Bool),
399            ExprKind::BinaryApp {
400                op: BinaryOp::Add | BinaryOp::Mul | BinaryOp::Sub,
401                ..
402            } => Some(Type::Long),
403            ExprKind::BinaryApp {
404                op:
405                    BinaryOp::Contains
406                    | BinaryOp::ContainsAll
407                    | BinaryOp::ContainsAny
408                    | BinaryOp::Eq
409                    | BinaryOp::In
410                    | BinaryOp::Less
411                    | BinaryOp::LessEq,
412                ..
413            } => Some(Type::Bool),
414            ExprKind::BinaryApp {
415                op: BinaryOp::HasTag,
416                ..
417            } => Some(Type::Bool),
418            ExprKind::ExtensionFunctionApp { fn_name, .. } => extensions
419                .func(fn_name)
420                .ok()?
421                .return_type()
422                .map(|rty| rty.clone().into()),
423            // We could try to be more complete here, but we can't do all that
424            // much better without evaluating the argument. Even if we know it's
425            // a record `Type::Record` tells us nothing about the type of the
426            // attribute.
427            ExprKind::GetAttr { .. } => None,
428            // similarly to `GetAttr`
429            ExprKind::BinaryApp {
430                op: BinaryOp::GetTag,
431                ..
432            } => None,
433            ExprKind::HasAttr { .. } => Some(Type::Bool),
434            ExprKind::ExtHasAttr { .. } => Some(Type::Bool),
435            ExprKind::Like { .. } => Some(Type::Bool),
436            ExprKind::Is { .. } => Some(Type::Bool),
437            ExprKind::Set(_) => Some(Type::Set),
438            ExprKind::Record(_) => Some(Type::Record),
439            #[cfg(feature = "tolerant-ast")]
440            ExprKind::Error { .. } => None,
441        }
442    }
443
444    /// Converts an `Expr<V>` to `B::Expr` using the provided builder.
445    ///
446    /// Preserves source location information and recursively transforms each expression node.
447    /// Note: Data may be cloned if the source expression is retained elsewhere.
448    /// Convert this expression to a `B::Expr`, where `B` is a fallible builder.
449    /// Uses `try_call_extension_fn` for extension function calls.
450    pub fn try_into_expr<B: expr_builder::ExprBuilder>(self) -> Result<B::Expr, B::BuildError>
451    where
452        T: Clone,
453    {
454        let builder = B::new().with_maybe_source_loc(self.source_loc());
455        match self.into_expr_kind() {
456            ExprKind::Lit(lit) => Ok(builder.val(lit)),
457            ExprKind::Var(var) => Ok(builder.var(var)),
458            ExprKind::Slot(slot) => Ok(builder.slot(slot)),
459            ExprKind::Unknown(u) => Ok(builder.unknown(u)),
460            ExprKind::If {
461                test_expr,
462                then_expr,
463                else_expr,
464            } => Ok(builder.ite(
465                Arc::unwrap_or_clone(test_expr).try_into_expr::<B>()?,
466                Arc::unwrap_or_clone(then_expr).try_into_expr::<B>()?,
467                Arc::unwrap_or_clone(else_expr).try_into_expr::<B>()?,
468            )),
469            ExprKind::And { left, right } => Ok(builder.and(
470                Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
471                Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
472            )),
473            ExprKind::Or { left, right } => Ok(builder.or(
474                Arc::unwrap_or_clone(left).try_into_expr::<B>()?,
475                Arc::unwrap_or_clone(right).try_into_expr::<B>()?,
476            )),
477            ExprKind::UnaryApp { op, arg } => {
478                Ok(builder.unary_app(op, Arc::unwrap_or_clone(arg).try_into_expr::<B>()?))
479            }
480            ExprKind::BinaryApp { op, arg1, arg2 } => Ok(builder.binary_app(
481                op,
482                Arc::unwrap_or_clone(arg1).try_into_expr::<B>()?,
483                Arc::unwrap_or_clone(arg2).try_into_expr::<B>()?,
484            )),
485            ExprKind::ExtensionFunctionApp { fn_name, args } => {
486                let args: Vec<_> = Arc::unwrap_or_clone(args)
487                    .into_iter()
488                    .map(|e| e.try_into_expr::<B>())
489                    .collect::<Result<_, _>>()?;
490                builder.call_extension_fn(fn_name, args)
491            }
492            ExprKind::GetAttr { expr, attr } => {
493                Ok(builder.get_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
494            }
495            ExprKind::HasAttr { expr, attr } => {
496                Ok(builder.has_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attr))
497            }
498            ExprKind::ExtHasAttr { expr, attrs } => {
499                Ok(builder
500                    .extended_has_attr(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, attrs))
501            }
502            ExprKind::Like { expr, pattern } => {
503                Ok(builder.like(Arc::unwrap_or_clone(expr).try_into_expr::<B>()?, pattern))
504            }
505            ExprKind::Is { expr, entity_type } => Ok(builder.is_entity_type(
506                Arc::unwrap_or_clone(expr).try_into_expr::<B>()?,
507                entity_type,
508            )),
509            ExprKind::Set(set) => Ok(builder.set(
510                Arc::unwrap_or_clone(set)
511                    .into_iter()
512                    .map(|e| e.try_into_expr::<B>())
513                    .collect::<Result<Vec<_>, _>>()?,
514            )),
515            #[expect(
516                clippy::unwrap_used,
517                reason = "`map` is a map, so it will not have duplicate keys, so the `.record()` constructor cannot error"
518            )]
519            ExprKind::Record(map) => Ok(builder
520                .record(
521                    Arc::unwrap_or_clone(map)
522                        .into_iter()
523                        .map(|(k, v)| Ok((k, v.try_into_expr::<B>()?)))
524                        .collect::<Result<Vec<_>, _>>()?,
525                )
526                .unwrap()),
527            #[cfg(feature = "tolerant-ast")]
528            #[expect(
529                clippy::unwrap_used,
530                reason = "error type is Infallible so can never happen"
531            )]
532            ExprKind::Error { .. } => Ok(builder
533                .error(ParseErrors::singleton(ToASTError::new(
534                    ToASTErrorKind::ASTErrorNode,
535                    Some(Loc::new(0..1, "AST_ERROR_NODE".into())),
536                )))
537                .unwrap()), // we could have unwrap_infallible + trait bound  but attributes in where clauses are unstable
538        }
539    }
540
541    /// Convert this expression to a `B::Expr`, where `B` is an infallible builder.
542    pub fn into_expr<B: expr_builder::ExprBuilder>(self) -> B::Expr
543    where
544        T: Clone,
545        B::BuildError: IsInfallible,
546    {
547        self.try_into_expr::<B>().unwrap_infallible()
548    }
549}
550
551#[expect(
552    clippy::should_implement_trait,
553    reason = "the names of arithmetic constructors alias with those of certain trait methods such as `add` of `std::ops::Add`"
554)]
555impl Expr {
556    /// Create an `Expr` that's just a single `Literal`.
557    ///
558    /// Note that you can pass this a `Literal`, an `Integer`, a `String`, etc.
559    pub fn val(v: impl Into<Literal>) -> Self {
560        ExprBuilder::new().val(v)
561    }
562
563    /// Create an `Expr` that's just a single `Unknown`.
564    pub fn unknown(u: Unknown) -> Self {
565        ExprBuilder::new().unknown(u)
566    }
567
568    /// Create an `Expr` that's just this literal `Var`
569    pub fn var(v: Var) -> Self {
570        ExprBuilder::new().var(v)
571    }
572
573    /// Create an `Expr` that's just this `SlotId`
574    pub fn slot(s: SlotId) -> Self {
575        ExprBuilder::new().slot(s)
576    }
577
578    /// Create a ternary (if-then-else) `Expr`.
579    ///
580    /// `test_expr` must evaluate to a Bool type
581    pub fn ite(test_expr: Expr, then_expr: Expr, else_expr: Expr) -> Self {
582        ExprBuilder::new().ite(test_expr, then_expr, else_expr)
583    }
584
585    /// Create a ternary (if-then-else) `Expr`.
586    /// Takes `Arc`s instead of owned `Expr`s.
587    /// `test_expr` must evaluate to a Bool type
588    pub fn ite_arc(test_expr: Arc<Expr>, then_expr: Arc<Expr>, else_expr: Arc<Expr>) -> Self {
589        ExprBuilder::new().ite_arc(test_expr, then_expr, else_expr)
590    }
591
592    /// Create a 'not' expression. `e` must evaluate to Bool type
593    pub fn not(e: Expr) -> Self {
594        ExprBuilder::new().not(e)
595    }
596
597    /// Create a '==' expression
598    pub fn is_eq(e1: Expr, e2: Expr) -> Self {
599        ExprBuilder::new().is_eq(e1, e2)
600    }
601
602    /// Create a '!=' expression
603    pub fn noteq(e1: Expr, e2: Expr) -> Self {
604        ExprBuilder::new().noteq(e1, e2)
605    }
606
607    /// Create an 'and' expression. Arguments must evaluate to Bool type
608    pub fn and(e1: Expr, e2: Expr) -> Self {
609        ExprBuilder::new().and(e1, e2)
610    }
611
612    /// Create an 'or' expression. Arguments must evaluate to Bool type
613    pub fn or(e1: Expr, e2: Expr) -> Self {
614        ExprBuilder::new().or(e1, e2)
615    }
616
617    /// Create a '<' expression. Arguments must evaluate to Long type
618    pub fn less(e1: Expr, e2: Expr) -> Self {
619        ExprBuilder::new().less(e1, e2)
620    }
621
622    /// Create a '<=' expression. Arguments must evaluate to Long type
623    pub fn lesseq(e1: Expr, e2: Expr) -> Self {
624        ExprBuilder::new().lesseq(e1, e2)
625    }
626
627    /// Create a '>' expression. Arguments must evaluate to Long type
628    pub fn greater(e1: Expr, e2: Expr) -> Self {
629        ExprBuilder::new().greater(e1, e2)
630    }
631
632    /// Create a '>=' expression. Arguments must evaluate to Long type
633    pub fn greatereq(e1: Expr, e2: Expr) -> Self {
634        ExprBuilder::new().greatereq(e1, e2)
635    }
636
637    /// Create an 'add' expression. Arguments must evaluate to Long type
638    pub fn add(e1: Expr, e2: Expr) -> Self {
639        ExprBuilder::new().add(e1, e2)
640    }
641
642    /// Create a 'sub' expression. Arguments must evaluate to Long type
643    pub fn sub(e1: Expr, e2: Expr) -> Self {
644        ExprBuilder::new().sub(e1, e2)
645    }
646
647    /// Create a 'mul' expression. Arguments must evaluate to Long type
648    pub fn mul(e1: Expr, e2: Expr) -> Self {
649        ExprBuilder::new().mul(e1, e2)
650    }
651
652    /// Create a 'neg' expression. `e` must evaluate to Long type.
653    pub fn neg(e: Expr) -> Self {
654        ExprBuilder::new().neg(e)
655    }
656
657    /// Create an 'in' expression. First argument must evaluate to Entity type.
658    /// Second argument must evaluate to either Entity type or Set type where
659    /// all set elements have Entity type.
660    pub fn is_in(e1: Expr, e2: Expr) -> Self {
661        ExprBuilder::new().is_in(e1, e2)
662    }
663
664    /// Create a `contains` expression.
665    /// First argument must have Set type.
666    pub fn contains(e1: Expr, e2: Expr) -> Self {
667        ExprBuilder::new().contains(e1, e2)
668    }
669
670    /// Create a `containsAll` expression. Arguments must evaluate to Set type
671    pub fn contains_all(e1: Expr, e2: Expr) -> Self {
672        ExprBuilder::new().contains_all(e1, e2)
673    }
674
675    /// Create a `containsAny` expression. Arguments must evaluate to Set type
676    pub fn contains_any(e1: Expr, e2: Expr) -> Self {
677        ExprBuilder::new().contains_any(e1, e2)
678    }
679
680    /// Create a `isEmpty` expression. Argument must evaluate to Set type
681    pub fn is_empty(e: Expr) -> Self {
682        ExprBuilder::new().is_empty(e)
683    }
684
685    /// Create a `getTag` expression.
686    /// `expr` must evaluate to Entity type, `tag` must evaluate to String type.
687    pub fn get_tag(expr: Expr, tag: Expr) -> Self {
688        ExprBuilder::new().get_tag(expr, tag)
689    }
690
691    /// Create a `hasTag` expression.
692    /// `expr` must evaluate to Entity type, `tag` must evaluate to String type.
693    pub fn has_tag(expr: Expr, tag: Expr) -> Self {
694        ExprBuilder::new().has_tag(expr, tag)
695    }
696
697    /// Create an `Expr` which evaluates to a Set of the given `Expr`s
698    pub fn set(exprs: impl IntoIterator<Item = Expr>) -> Self {
699        ExprBuilder::new().set(exprs)
700    }
701
702    /// Create an `Expr` which evaluates to a Record with the given (key, value) pairs.
703    pub fn record(
704        pairs: impl IntoIterator<Item = (SmolStr, Expr)>,
705    ) -> Result<Self, ExpressionConstructionError> {
706        ExprBuilder::new().record(pairs)
707    }
708
709    /// Create an `Expr` which evaluates to a Record with the given key-value mapping.
710    ///
711    /// If you have an iterator of pairs, generally prefer calling
712    /// `Expr::record()` instead of `.collect()`-ing yourself and calling this,
713    /// potentially for efficiency reasons but also because `Expr::record()`
714    /// will properly handle duplicate keys but your own `.collect()` will not
715    /// (by default).
716    pub fn record_arc(map: Arc<BTreeMap<SmolStr, Expr>>) -> Self {
717        ExprBuilder::new().record_arc(map)
718    }
719
720    /// Create an `Expr` which calls the extension function with the given
721    /// `Name` on `args`
722    pub fn call_extension_fn(fn_name: Name, args: Vec<Expr>) -> Self {
723        ExprBuilder::new()
724            .call_extension_fn(fn_name, args)
725            .unwrap_infallible()
726    }
727
728    /// Create an application `Expr` which applies the given built-in unary
729    /// operator to the given `arg`
730    pub fn unary_app(op: impl Into<UnaryOp>, arg: Expr) -> Self {
731        ExprBuilder::new().unary_app(op, arg)
732    }
733
734    /// Create an application `Expr` which applies the given built-in binary
735    /// operator to `arg1` and `arg2`
736    pub fn binary_app(op: impl Into<BinaryOp>, arg1: Expr, arg2: Expr) -> Self {
737        ExprBuilder::new().binary_app(op, arg1, arg2)
738    }
739
740    /// Create an `Expr` which gets a given attribute of a given `Entity` or record.
741    ///
742    /// `expr` must evaluate to either Entity or Record type
743    pub fn get_attr(expr: Expr, attr: SmolStr) -> Self {
744        ExprBuilder::new().get_attr(expr, attr)
745    }
746
747    /// Create an `Expr` which tests for the existence of a given
748    /// attribute on a given `Entity` or record.
749    ///
750    /// `expr` must evaluate to either Entity or Record type
751    pub fn has_attr(expr: Expr, attr: SmolStr) -> Self {
752        ExprBuilder::new().has_attr(expr, attr)
753    }
754
755    /// Create an `Expr` which tests for the existence of a given
756    /// sequence of attributes on a given `Entity` or record.
757    ///
758    /// `expr` must evaluate to either Entity or Record type
759    pub fn extended_has_attr(expr: Expr, attrs: NonEmpty<SmolStr>) -> Self {
760        ExprBuilder::new().extended_has_attr(expr, attrs)
761    }
762
763    /// Create a 'like' expression.
764    ///
765    /// `expr` must evaluate to a String type
766    pub fn like(expr: Expr, pattern: Pattern) -> Self {
767        ExprBuilder::new().like(expr, pattern)
768    }
769
770    /// Create an `is` expression.
771    pub fn is_entity_type(expr: Expr, entity_type: EntityType) -> Self {
772        ExprBuilder::new().is_entity_type(expr, entity_type)
773    }
774
775    /// Check if an expression contains any symbolic unknowns
776    pub fn contains_unknown(&self) -> bool {
777        self.subexpressions()
778            .any(|e| matches!(e.expr_kind(), ExprKind::Unknown(_)))
779    }
780
781    /// Get all unknowns in an expression
782    pub fn unknowns(&self) -> impl Iterator<Item = &Unknown> {
783        self.subexpressions()
784            .filter_map(|subexpr| match subexpr.expr_kind() {
785                ExprKind::Unknown(u) => Some(u),
786                _ => None,
787            })
788    }
789
790    /// Substitute unknowns with concrete values.
791    ///
792    /// Ignores unmapped unknowns.
793    /// Ignores type annotations on unknowns.
794    /// Note that there might be "undiscovered unknowns" in the Expr, which
795    /// this function does not notice if evaluation of this Expr did not
796    /// traverse all entities and attributes during evaluation, leading to
797    /// this function only substituting one unknown at a time.
798    pub fn substitute(&self, definitions: &HashMap<SmolStr, Value>) -> Expr {
799        match self.substitute_general::<UntypedSubstitution>(definitions) {
800            Ok(e) => e,
801            Err(empty) => match empty {},
802        }
803    }
804
805    /// Substitute unknowns with concrete values.
806    ///
807    /// Ignores unmapped unknowns.
808    /// Errors if the substituted value does not match the type annotation on the unknown.
809    /// Note that there might be "undiscovered unknowns" in the Expr, which
810    /// this function does not notice if evaluation of this Expr did not
811    /// traverse all entities and attributes during evaluation, leading to
812    /// this function only substituting one unknown at a time.
813    pub fn substitute_typed(
814        &self,
815        definitions: &HashMap<SmolStr, Value>,
816    ) -> Result<Expr, SubstitutionError> {
817        self.substitute_general::<TypedSubstitution>(definitions)
818    }
819
820    /// Substitute unknowns with values
821    ///
822    /// Generic over the function implementing the substitution to allow for multiple error behaviors
823    fn substitute_general<T: SubstitutionFunction>(
824        &self,
825        definitions: &HashMap<SmolStr, Value>,
826    ) -> Result<Expr, T::Err> {
827        match self.expr_kind() {
828            ExprKind::Lit(_) => Ok(self.clone()),
829            ExprKind::Unknown(u @ Unknown { name, .. }) => T::substitute(u, definitions.get(name)),
830            ExprKind::Var(_) => Ok(self.clone()),
831            ExprKind::Slot(_) => Ok(self.clone()),
832            ExprKind::If {
833                test_expr,
834                then_expr,
835                else_expr,
836            } => Ok(Expr::ite(
837                test_expr.substitute_general::<T>(definitions)?,
838                then_expr.substitute_general::<T>(definitions)?,
839                else_expr.substitute_general::<T>(definitions)?,
840            )),
841            ExprKind::And { left, right } => Ok(Expr::and(
842                left.substitute_general::<T>(definitions)?,
843                right.substitute_general::<T>(definitions)?,
844            )),
845            ExprKind::Or { left, right } => Ok(Expr::or(
846                left.substitute_general::<T>(definitions)?,
847                right.substitute_general::<T>(definitions)?,
848            )),
849            ExprKind::UnaryApp { op, arg } => Ok(Expr::unary_app(
850                *op,
851                arg.substitute_general::<T>(definitions)?,
852            )),
853            ExprKind::BinaryApp { op, arg1, arg2 } => Ok(Expr::binary_app(
854                *op,
855                arg1.substitute_general::<T>(definitions)?,
856                arg2.substitute_general::<T>(definitions)?,
857            )),
858            ExprKind::ExtensionFunctionApp { fn_name, args } => {
859                let args = args
860                    .iter()
861                    .map(|e| e.substitute_general::<T>(definitions))
862                    .collect::<Result<Vec<Expr>, _>>()?;
863
864                Ok(Expr::call_extension_fn(fn_name.clone(), args))
865            }
866            ExprKind::GetAttr { expr, attr } => Ok(Expr::get_attr(
867                expr.substitute_general::<T>(definitions)?,
868                attr.clone(),
869            )),
870            ExprKind::HasAttr { expr, attr } => Ok(Expr::has_attr(
871                expr.substitute_general::<T>(definitions)?,
872                attr.clone(),
873            )),
874            ExprKind::ExtHasAttr { expr, attrs } => Ok(Expr::extended_has_attr(
875                expr.substitute_general::<T>(definitions)?,
876                attrs.clone(),
877            )),
878            ExprKind::Like { expr, pattern } => Ok(Expr::like(
879                expr.substitute_general::<T>(definitions)?,
880                pattern.clone(),
881            )),
882            ExprKind::Set(members) => {
883                let members = members
884                    .iter()
885                    .map(|e| e.substitute_general::<T>(definitions))
886                    .collect::<Result<Vec<_>, _>>()?;
887                Ok(Expr::set(members))
888            }
889            ExprKind::Record(map) => {
890                let map = map
891                    .iter()
892                    .map(|(name, e)| Ok((name.clone(), e.substitute_general::<T>(definitions)?)))
893                    .collect::<Result<BTreeMap<_, _>, _>>()?;
894                #[expect(
895                    clippy::expect_used,
896                    reason = "cannot have a duplicate key because the input was already a BTreeMap"
897                )]
898                Ok(Expr::record(map)
899                    .expect("cannot have a duplicate key because the input was already a BTreeMap"))
900            }
901            ExprKind::Is { expr, entity_type } => Ok(Expr::is_entity_type(
902                expr.substitute_general::<T>(definitions)?,
903                entity_type.clone(),
904            )),
905            #[cfg(feature = "tolerant-ast")]
906            ExprKind::Error { .. } => Ok(self.clone()),
907        }
908    }
909
910    /// Validate the expression is well-formed according to internal invariants.
911    /// This is useful if you obtained an AST without parsing from Cedar text, but want to ensure
912    /// the invariant obtained from parsing hold. Essentially, [`try_validate`] checks that
913    /// this is a "syntactically valid" expression that could have been constructed by parsing.
914    ///
915    /// The invariants being checked are:
916    /// - The name of the function in a function call is a known extension.
917    /// - If the function call must be a "method style" call, then its arguments are non-empty
918    /// - extended has only uses valid identifiers in the attributes
919    ///
920    ///
921    /// Other invariants guaranteed for the AST (a parseable expression) are maintained
922    /// structurally: well-formed ids, expression structure and absence of duplicates in
923    /// records.
924    ///
925    /// This does not check that the arity of the function call is correct: parsing does not
926    /// guarantee this.
927    pub fn try_validate(self) -> Result<Self, ExprValidationError> {
928        for sub in self.subexpressions() {
929            match sub.expr_kind() {
930                ExprKind::ExtensionFunctionApp { fn_name, args } => {
931                    // Invariant: fn_name must be a known extension function
932                    let ext_fn = Extensions::all_available().func(fn_name).map_err(|_| {
933                        ExprValidationError(format!("unknown extension function `{fn_name}`"))
934                    })?;
935                    // Invariant: if fn_name is MethodStyle then args must be non-empty
936                    if ext_fn.style() == CallStyle::MethodStyle && args.is_empty() {
937                        return Err(ExprValidationError(format!(
938                            "method-style extension function `{fn_name}` requires a receiver argument"
939                        )));
940                    }
941                    // **NOT** an invariant of parsed ASTs: arity is correct.
942                }
943                ExprKind::ExtHasAttr { attrs, .. } => {
944                    for attr in attrs {
945                        if !is_normalized_ident(attr) {
946                            return Err(ExprValidationError(format!(
947                                "extended has attribute `{attr}` is not a valid identifier"
948                            )));
949                        }
950                    }
951                }
952                _ => {}
953            }
954        }
955        Ok(self)
956    }
957}
958
959/// A trait for customizing the error behavior of substitution
960trait SubstitutionFunction {
961    /// The potential errors this substitution function can return
962    type Err;
963    /// The function for implementing the substitution.
964    ///
965    /// Takes the expression being substituted,
966    /// The substitution from the map (if present)
967    /// and the type annotation from the unknown (if present)
968    fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err>;
969}
970
971struct TypedSubstitution {}
972
973impl SubstitutionFunction for TypedSubstitution {
974    type Err = SubstitutionError;
975
976    fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
977        match (substitute, &value.type_annotation) {
978            (None, _) => Ok(Expr::unknown(value.clone())),
979            (Some(v), None) => Ok(v.clone().into()),
980            (Some(v), Some(t)) => {
981                if v.type_of() == *t {
982                    Ok(v.clone().into())
983                } else {
984                    Err(SubstitutionError::TypeError {
985                        expected: t.clone(),
986                        actual: v.type_of(),
987                    })
988                }
989            }
990        }
991    }
992}
993
994struct UntypedSubstitution {}
995
996impl SubstitutionFunction for UntypedSubstitution {
997    type Err = std::convert::Infallible;
998
999    fn substitute(value: &Unknown, substitute: Option<&Value>) -> Result<Expr, Self::Err> {
1000        Ok(substitute
1001            .map(|v| v.clone().into())
1002            .unwrap_or_else(|| Expr::unknown(value.clone())))
1003    }
1004}
1005
1006impl<T: Clone> std::fmt::Display for Expr<T> {
1007    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1008        // To avoid code duplication between pretty-printers for AST Expr and EST Expr,
1009        // we just convert to EST and use the EST pretty-printer.
1010        // Note that converting AST->EST is lossless and infallible.
1011        write!(f, "{}", self.clone().into_expr::<crate::est::Builder>())
1012    }
1013}
1014
1015impl<T: Clone> BoundedDisplay for Expr<T> {
1016    fn fmt(&self, f: &mut impl std::fmt::Write, n: Option<usize>) -> std::fmt::Result {
1017        // Like the `std::fmt::Display` impl, we convert to EST and use the EST
1018        // pretty-printer. Note that converting AST->EST is lossless and infallible.
1019        BoundedDisplay::fmt(&self.clone().into_expr::<crate::est::Builder>(), f, n)
1020    }
1021}
1022
1023impl std::str::FromStr for Expr {
1024    type Err = ParseErrors;
1025
1026    fn from_str(s: &str) -> Result<Expr, Self::Err> {
1027        crate::parser::parse_expr(s)
1028    }
1029}
1030
1031/// Enum for errors encountered during substitution
1032#[derive(Debug, Clone, Diagnostic, Error)]
1033pub enum SubstitutionError {
1034    /// The supplied value did not match the type annotation on the unknown.
1035    #[error("expected a value of type {expected}, got a value of type {actual}")]
1036    TypeError {
1037        /// The expected type, ie: the type the unknown was annotated with
1038        expected: Type,
1039        /// The type of the provided value
1040        actual: Type,
1041    },
1042}
1043
1044/// Representation of a partial-evaluation Unknown at the AST level
1045#[derive(Hash, Debug, Clone, PartialEq, Eq)]
1046pub struct Unknown {
1047    /// The name of the unknown
1048    pub name: SmolStr,
1049    /// The type of the values that can be substituted in for the unknown.
1050    /// If `None`, we have no type annotation, and thus a value of any type can
1051    /// be substituted.
1052    pub type_annotation: Option<Type>,
1053}
1054
1055impl Unknown {
1056    /// Create a new untyped `Unknown`
1057    pub fn new_untyped(name: impl Into<SmolStr>) -> Self {
1058        Self {
1059            name: name.into(),
1060            type_annotation: None,
1061        }
1062    }
1063
1064    /// Create a new `Unknown` with type annotation. (Only values of the given
1065    /// type can be substituted.)
1066    pub fn new_with_type(name: impl Into<SmolStr>, ty: Type) -> Self {
1067        Self {
1068            name: name.into(),
1069            type_annotation: Some(ty),
1070        }
1071    }
1072}
1073
1074impl std::fmt::Display for Unknown {
1075    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076        // Like the Display impl for Expr, we delegate to the EST pretty-printer,
1077        // to avoid code duplication
1078        write!(
1079            f,
1080            "{}",
1081            Expr::unknown(self.clone()).into_expr::<crate::est::Builder>()
1082        )
1083    }
1084}
1085
1086/// Builder for constructing `Expr` objects annotated with some `data`
1087/// (possibly taking default value) and optionally a `source_loc`.
1088#[derive(Clone, Debug)]
1089pub struct ExprBuilder<T> {
1090    source_loc: Option<Loc>,
1091    data: T,
1092}
1093
1094impl<T: Default + Clone> expr_builder::ExprBuilderInfallibleBuild for ExprBuilder<T> {}
1095
1096impl<T: Default + Clone> expr_builder::ExprBuilder for ExprBuilder<T> {
1097    type Expr = Expr<T>;
1098
1099    type Data = T;
1100
1101    type BuildError = Infallible;
1102
1103    #[cfg(feature = "tolerant-ast")]
1104    type ErrorType = ParseErrors;
1105
1106    fn loc(&self) -> Option<&Loc> {
1107        self.source_loc.as_ref()
1108    }
1109
1110    fn data(&self) -> &Self::Data {
1111        &self.data
1112    }
1113
1114    fn with_data(data: T) -> Self {
1115        Self {
1116            source_loc: None,
1117            data,
1118        }
1119    }
1120
1121    fn with_maybe_source_loc(mut self, maybe_source_loc: Option<&Loc>) -> Self {
1122        self.source_loc = maybe_source_loc.cloned();
1123        self
1124    }
1125
1126    /// Create an `Expr` that's just a single `Literal`.
1127    ///
1128    /// Note that you can pass this a `Literal`, an `Integer`, a `String`, etc.
1129    fn val(self, v: impl Into<Literal>) -> Expr<T> {
1130        self.with_expr_kind(ExprKind::Lit(v.into()))
1131    }
1132
1133    /// Create an `Unknown` `Expr`
1134    fn unknown(self, u: Unknown) -> Expr<T> {
1135        self.with_expr_kind(ExprKind::Unknown(u))
1136    }
1137
1138    /// Create an `Expr` that's just this literal `Var`
1139    fn var(self, v: Var) -> Expr<T> {
1140        self.with_expr_kind(ExprKind::Var(v))
1141    }
1142
1143    /// Create an `Expr` that's just this `SlotId`
1144    fn slot(self, s: SlotId) -> Expr<T> {
1145        self.with_expr_kind(ExprKind::Slot(s))
1146    }
1147
1148    /// Create a ternary (if-then-else) `Expr`.
1149    /// Takes `Arc`s instead of owned `Expr`s.
1150    /// `test_expr` must evaluate to a Bool type
1151    fn ite_arc(
1152        self,
1153        test_expr: Arc<Expr<T>>,
1154        then_expr: Arc<Expr<T>>,
1155        else_expr: Arc<Expr<T>>,
1156    ) -> Expr<T> {
1157        self.with_expr_kind(ExprKind::If {
1158            test_expr,
1159            then_expr,
1160            else_expr,
1161        })
1162    }
1163
1164    /// Create a 'not' expression. `e` must evaluate to Bool type
1165    fn not(self, e: Expr<T>) -> Expr<T> {
1166        self.with_expr_kind(ExprKind::UnaryApp {
1167            op: UnaryOp::Not,
1168            arg: Arc::new(e),
1169        })
1170    }
1171
1172    /// Create a '==' expression
1173    fn is_eq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1174        self.with_expr_kind(ExprKind::BinaryApp {
1175            op: BinaryOp::Eq,
1176            arg1: Arc::new(e1),
1177            arg2: Arc::new(e2),
1178        })
1179    }
1180
1181    /// Create an 'and' expression. Arguments must evaluate to Bool type
1182    fn and(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1183        self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1184            (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1185                ExprKind::Lit(Literal::Bool(*b1 && *b2))
1186            }
1187            _ => ExprKind::And {
1188                left: Arc::new(e1),
1189                right: Arc::new(e2),
1190            },
1191        })
1192    }
1193
1194    /// Create an 'or' expression. Arguments must evaluate to Bool type
1195    fn or(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1196        self.with_expr_kind(match (&e1.expr_kind, &e2.expr_kind) {
1197            (ExprKind::Lit(Literal::Bool(b1)), ExprKind::Lit(Literal::Bool(b2))) => {
1198                ExprKind::Lit(Literal::Bool(*b1 || *b2))
1199            }
1200
1201            _ => ExprKind::Or {
1202                left: Arc::new(e1),
1203                right: Arc::new(e2),
1204            },
1205        })
1206    }
1207
1208    /// Create a '<' expression. Arguments must evaluate to Long type
1209    fn less(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1210        self.with_expr_kind(ExprKind::BinaryApp {
1211            op: BinaryOp::Less,
1212            arg1: Arc::new(e1),
1213            arg2: Arc::new(e2),
1214        })
1215    }
1216
1217    /// Create a '<=' expression. Arguments must evaluate to Long type
1218    fn lesseq(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1219        self.with_expr_kind(ExprKind::BinaryApp {
1220            op: BinaryOp::LessEq,
1221            arg1: Arc::new(e1),
1222            arg2: Arc::new(e2),
1223        })
1224    }
1225
1226    /// Create an 'add' expression. Arguments must evaluate to Long type
1227    fn add(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1228        self.with_expr_kind(ExprKind::BinaryApp {
1229            op: BinaryOp::Add,
1230            arg1: Arc::new(e1),
1231            arg2: Arc::new(e2),
1232        })
1233    }
1234
1235    /// Create a 'sub' expression. Arguments must evaluate to Long type
1236    fn sub(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1237        self.with_expr_kind(ExprKind::BinaryApp {
1238            op: BinaryOp::Sub,
1239            arg1: Arc::new(e1),
1240            arg2: Arc::new(e2),
1241        })
1242    }
1243
1244    /// Create a 'mul' expression. Arguments must evaluate to Long type
1245    fn mul(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1246        self.with_expr_kind(ExprKind::BinaryApp {
1247            op: BinaryOp::Mul,
1248            arg1: Arc::new(e1),
1249            arg2: Arc::new(e2),
1250        })
1251    }
1252
1253    /// Create a 'neg' expression. `e` must evaluate to Long type.
1254    fn neg(self, e: Expr<T>) -> Expr<T> {
1255        self.with_expr_kind(ExprKind::UnaryApp {
1256            op: UnaryOp::Neg,
1257            arg: Arc::new(e),
1258        })
1259    }
1260
1261    /// Create an 'in' expression. First argument must evaluate to Entity type.
1262    /// Second argument must evaluate to either Entity type or Set type where
1263    /// all set elements have Entity type.
1264    fn is_in_arc(self, arg1: Arc<Expr<T>>, arg2: Arc<Expr<T>>) -> Expr<T> {
1265        self.with_expr_kind(ExprKind::BinaryApp {
1266            op: BinaryOp::In,
1267            arg1,
1268            arg2,
1269        })
1270    }
1271
1272    /// Create a 'contains' expression.
1273    /// First argument must have Set type.
1274    fn contains(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1275        self.with_expr_kind(ExprKind::BinaryApp {
1276            op: BinaryOp::Contains,
1277            arg1: Arc::new(e1),
1278            arg2: Arc::new(e2),
1279        })
1280    }
1281
1282    /// Create a 'contains_all' expression. Arguments must evaluate to Set type
1283    fn contains_all(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1284        self.with_expr_kind(ExprKind::BinaryApp {
1285            op: BinaryOp::ContainsAll,
1286            arg1: Arc::new(e1),
1287            arg2: Arc::new(e2),
1288        })
1289    }
1290
1291    /// Create an 'contains_any' expression. Arguments must evaluate to Set type
1292    fn contains_any(self, e1: Expr<T>, e2: Expr<T>) -> Expr<T> {
1293        self.with_expr_kind(ExprKind::BinaryApp {
1294            op: BinaryOp::ContainsAny,
1295            arg1: Arc::new(e1),
1296            arg2: Arc::new(e2),
1297        })
1298    }
1299
1300    /// Create an 'is_empty' expression. Argument must evaluate to Set type
1301    fn is_empty(self, expr: Expr<T>) -> Expr<T> {
1302        self.with_expr_kind(ExprKind::UnaryApp {
1303            op: UnaryOp::IsEmpty,
1304            arg: Arc::new(expr),
1305        })
1306    }
1307
1308    /// Create a 'getTag' expression.
1309    /// `expr` must evaluate to Entity type, `tag` must evaluate to String type.
1310    fn get_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1311        self.with_expr_kind(ExprKind::BinaryApp {
1312            op: BinaryOp::GetTag,
1313            arg1: Arc::new(expr),
1314            arg2: Arc::new(tag),
1315        })
1316    }
1317
1318    /// Create a 'hasTag' expression.
1319    /// `expr` must evaluate to Entity type, `tag` must evaluate to String type.
1320    fn has_tag(self, expr: Expr<T>, tag: Expr<T>) -> Expr<T> {
1321        self.with_expr_kind(ExprKind::BinaryApp {
1322            op: BinaryOp::HasTag,
1323            arg1: Arc::new(expr),
1324            arg2: Arc::new(tag),
1325        })
1326    }
1327
1328    /// Create an `Expr` which evaluates to a Set of the given `Expr`s
1329    fn set(self, exprs: impl IntoIterator<Item = Expr<T>>) -> Expr<T> {
1330        self.with_expr_kind(ExprKind::Set(Arc::new(exprs.into_iter().collect())))
1331    }
1332
1333    /// Create an `Expr` which evaluates to a Record with the given (key, value) pairs.
1334    fn record(
1335        self,
1336        pairs: impl IntoIterator<Item = (SmolStr, Expr<T>)>,
1337    ) -> Result<Expr<T>, ExpressionConstructionError> {
1338        let mut map = BTreeMap::new();
1339        for (k, v) in pairs {
1340            match map.entry(k) {
1341                btree_map::Entry::Occupied(oentry) => {
1342                    return Err(expression_construction_errors::DuplicateKeyError {
1343                        key: oentry.key().clone(),
1344                        context: "in record literal",
1345                    }
1346                    .into());
1347                }
1348                btree_map::Entry::Vacant(ventry) => {
1349                    ventry.insert(v);
1350                }
1351            }
1352        }
1353        Ok(self.with_expr_kind(ExprKind::Record(Arc::new(map))))
1354    }
1355
1356    /// Create an `Expr` which calls the extension function with the given
1357    /// `Name` on `args`
1358    fn call_extension_fn(
1359        self,
1360        fn_name: Name,
1361        args: impl IntoIterator<Item = Expr<T>>,
1362    ) -> Result<Expr<T>, Infallible> {
1363        Ok(self.with_expr_kind(ExprKind::ExtensionFunctionApp {
1364            fn_name,
1365            args: Arc::new(args.into_iter().collect()),
1366        }))
1367    }
1368
1369    /// Create an application `Expr` which applies the given built-in unary
1370    /// operator to the given `arg`
1371    fn unary_app(self, op: impl Into<UnaryOp>, arg: Expr<T>) -> Expr<T> {
1372        self.with_expr_kind(ExprKind::UnaryApp {
1373            op: op.into(),
1374            arg: Arc::new(arg),
1375        })
1376    }
1377
1378    /// Create an application `Expr` which applies the given built-in binary
1379    /// operator to `arg1` and `arg2`
1380    fn binary_app(self, op: impl Into<BinaryOp>, arg1: Expr<T>, arg2: Expr<T>) -> Expr<T> {
1381        self.with_expr_kind(ExprKind::BinaryApp {
1382            op: op.into(),
1383            arg1: Arc::new(arg1),
1384            arg2: Arc::new(arg2),
1385        })
1386    }
1387
1388    /// Create an `Expr` which gets a given attribute of a given `Entity` or record.
1389    ///
1390    /// `expr` must evaluate to either Entity or Record type
1391    fn get_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1392        self.with_expr_kind(ExprKind::GetAttr { expr, attr })
1393    }
1394
1395    /// Create an `Expr` which tests for the existence of a given
1396    /// attribute on a given `Entity` or record.
1397    ///
1398    /// `expr` must evaluate to either Entity or Record type
1399    fn has_attr_arc(self, expr: Arc<Expr<T>>, attr: SmolStr) -> Expr<T> {
1400        self.with_expr_kind(ExprKind::HasAttr { expr, attr })
1401    }
1402
1403    /// Create a 'like' expression.
1404    ///
1405    /// `expr` must evaluate to a String type
1406    fn like(self, expr: Expr<T>, pattern: Pattern) -> Expr<T> {
1407        self.with_expr_kind(ExprKind::Like {
1408            expr: Arc::new(expr),
1409            pattern,
1410        })
1411    }
1412
1413    /// Create an 'is' expression.
1414    fn is_entity_type_arc(self, expr: Arc<Expr<T>>, entity_type: EntityType) -> Expr<T> {
1415        self.with_expr_kind(ExprKind::Is { expr, entity_type })
1416    }
1417
1418    /// Create an extended has expression directly in the AST without desugaring.
1419    fn extended_has_attr_arc(self, expr: Arc<Expr<T>>, attrs: NonEmpty<SmolStr>) -> Expr<T> {
1420        // If there's only one attribute, create a simple HasAttr node
1421        if attrs.tail.is_empty() {
1422            self.with_expr_kind(ExprKind::HasAttr {
1423                expr,
1424                attr: attrs.head,
1425            })
1426        } else {
1427            self.with_expr_kind(ExprKind::ExtHasAttr { expr, attrs })
1428        }
1429    }
1430
1431    /// Don't support AST Error nodes - return the error right back
1432    #[cfg(feature = "tolerant-ast")]
1433    fn error(self, parse_errors: ParseErrors) -> Result<Self::Expr, Self::ErrorType> {
1434        Err(parse_errors)
1435    }
1436}
1437
1438impl<T> ExprBuilder<T> {
1439    /// Construct an `Expr` containing the `data` and `source_loc` in this
1440    /// `ExprBuilder` and the given `ExprKind`.
1441    pub fn with_expr_kind(self, expr_kind: ExprKind<T>) -> Expr<T> {
1442        Expr::new(expr_kind, self.source_loc, self.data)
1443    }
1444
1445    /// Create an `Expr` which evaluates to a Record with the given key-value mapping.
1446    ///
1447    /// If you have an iterator of pairs, generally prefer calling `.record()`
1448    /// instead of `.collect()`-ing yourself and calling this, potentially for
1449    /// efficiency reasons but also because `.record()` will properly handle
1450    /// duplicate keys but your own `.collect()` will not (by default).
1451    pub fn record_arc(self, map: Arc<BTreeMap<SmolStr, Expr<T>>>) -> Expr<T> {
1452        self.with_expr_kind(ExprKind::Record(map))
1453    }
1454}
1455
1456impl<T: Clone + Default> ExprBuilder<T> {
1457    /// Utility used the validator to get an expression with the same source
1458    /// location as an existing expression. This is done when reconstructing the
1459    /// `Expr` with type information.
1460    pub fn with_same_source_loc<U>(self, expr: &Expr<U>) -> Self {
1461        self.with_maybe_source_loc(expr.source_loc.as_ref())
1462    }
1463}
1464
1465/// Error returned by [`Expr::try_validate`] for internal invariant violations.
1466#[derive(Error, Debug, Clone, Diagnostic, PartialEq, Eq)]
1467#[error("invalid expression: {0}")]
1468pub struct ExprValidationError(String);
1469
1470/// Errors when constructing an expression
1471//
1472// CAUTION: this type is publicly exported in `cedar-policy`.
1473// Don't make fields `pub`, don't make breaking changes, and use caution
1474// when adding public methods.
1475#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1476pub enum ExpressionConstructionError {
1477    /// The same key occurred two or more times
1478    #[error(transparent)]
1479    #[diagnostic(transparent)]
1480    DuplicateKey(#[from] expression_construction_errors::DuplicateKeyError),
1481}
1482
1483/// Error subtypes for [`ExpressionConstructionError`]
1484pub mod expression_construction_errors {
1485    use miette::Diagnostic;
1486    use smol_str::SmolStr;
1487    use thiserror::Error;
1488
1489    /// The same key occurred two or more times
1490    //
1491    // CAUTION: this type is publicly exported in `cedar-policy`.
1492    // Don't make fields `pub`, don't make breaking changes, and use caution
1493    // when adding public methods.
1494    #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
1495    #[error("duplicate key `{key}` {context}")]
1496    pub struct DuplicateKeyError {
1497        /// The key which occurred two or more times
1498        pub(crate) key: SmolStr,
1499        /// Information about where the duplicate key occurred (e.g., "in record literal")
1500        pub(crate) context: &'static str,
1501    }
1502
1503    impl DuplicateKeyError {
1504        /// Get the key which occurred two or more times
1505        pub fn key(&self) -> &str {
1506            &self.key
1507        }
1508
1509        /// Make a new error with an updated `context` field
1510        pub(crate) fn with_context(self, context: &'static str) -> Self {
1511            Self { context, ..self }
1512        }
1513    }
1514}
1515
1516/// A new type wrapper around `Expr` that provides `Eq` and `Hash`
1517/// implementations that ignore any source information or other generic data
1518/// used to annotate the `Expr`.
1519#[derive(Debug, Clone)]
1520pub struct ExprShapeOnly<'a, T: Clone = ()>(Cow<'a, Expr<T>>);
1521
1522impl<'a, T: Clone> ExprShapeOnly<'a, T> {
1523    /// Construct an `ExprShapeOnly` from a borrowed `Expr`. The `Expr` is not
1524    /// modified, but any comparisons on the resulting `ExprShapeOnly` will
1525    /// ignore source information and generic data.
1526    pub fn new_from_borrowed(e: &'a Expr<T>) -> ExprShapeOnly<'a, T> {
1527        ExprShapeOnly(Cow::Borrowed(e))
1528    }
1529
1530    /// Construct an `ExprShapeOnly` from an owned `Expr`. The `Expr` is not
1531    /// modified, but any comparisons on the resulting `ExprShapeOnly` will
1532    /// ignore source information and generic data.
1533    pub fn new_from_owned(e: Expr<T>) -> ExprShapeOnly<'a, T> {
1534        ExprShapeOnly(Cow::Owned(e))
1535    }
1536}
1537
1538impl<T: Clone> PartialEq for ExprShapeOnly<'_, T> {
1539    fn eq(&self, other: &Self) -> bool {
1540        self.0.eq_shape(&other.0)
1541    }
1542}
1543
1544impl<T: Clone> Eq for ExprShapeOnly<'_, T> {}
1545
1546impl<T: Clone> Hash for ExprShapeOnly<'_, T> {
1547    fn hash<H: Hasher>(&self, state: &mut H) {
1548        self.0.hash_shape(state);
1549    }
1550}
1551
1552impl<T: Clone> PartialOrd for ExprShapeOnly<'_, T> {
1553    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1554        Some(self.cmp(other))
1555    }
1556}
1557
1558impl<T: Clone> Ord for ExprShapeOnly<'_, T> {
1559    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1560        self.0.cmp_shape(&other.0)
1561    }
1562}
1563
1564impl<T> Expr<T> {
1565    /// Return true if this expression (recursively) has the same expression
1566    /// kind as the argument expression. This accounts for the full recursive
1567    /// shape of the expression, but does not consider source information or any
1568    /// generic data annotated on expression. This should behave the same as the
1569    /// default implementation of `Eq` before source information and generic
1570    /// data were added.
1571    pub fn eq_shape<U>(&self, other: &Expr<U>) -> bool {
1572        use ExprKind::*;
1573        match (self.expr_kind(), other.expr_kind()) {
1574            (Lit(lit), Lit(lit1)) => lit == lit1,
1575            (Var(v), Var(v1)) => v == v1,
1576            (Slot(s), Slot(s1)) => s == s1,
1577            (
1578                Unknown(self::Unknown {
1579                    name: name1,
1580                    type_annotation: ta_1,
1581                }),
1582                Unknown(self::Unknown {
1583                    name: name2,
1584                    type_annotation: ta_2,
1585                }),
1586            ) => (name1 == name2) && (ta_1 == ta_2),
1587            (
1588                If {
1589                    test_expr,
1590                    then_expr,
1591                    else_expr,
1592                },
1593                If {
1594                    test_expr: test_expr1,
1595                    then_expr: then_expr1,
1596                    else_expr: else_expr1,
1597                },
1598            ) => {
1599                test_expr.eq_shape(test_expr1)
1600                    && then_expr.eq_shape(then_expr1)
1601                    && else_expr.eq_shape(else_expr1)
1602            }
1603            (
1604                And { left, right },
1605                And {
1606                    left: left1,
1607                    right: right1,
1608                },
1609            )
1610            | (
1611                Or { left, right },
1612                Or {
1613                    left: left1,
1614                    right: right1,
1615                },
1616            ) => left.eq_shape(left1) && right.eq_shape(right1),
1617            (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1618                op == op1 && arg.eq_shape(arg1)
1619            }
1620            (
1621                BinaryApp { op, arg1, arg2 },
1622                BinaryApp {
1623                    op: op1,
1624                    arg1: arg11,
1625                    arg2: arg21,
1626                },
1627            ) => op == op1 && arg1.eq_shape(arg11) && arg2.eq_shape(arg21),
1628            (
1629                ExtensionFunctionApp { fn_name, args },
1630                ExtensionFunctionApp {
1631                    fn_name: fn_name1,
1632                    args: args1,
1633                },
1634            ) => {
1635                fn_name == fn_name1
1636                    && args.len() == args1.len()
1637                    && args.iter().zip(args1.iter()).all(|(a, a1)| a.eq_shape(a1))
1638            }
1639            (
1640                GetAttr { expr, attr },
1641                GetAttr {
1642                    expr: expr1,
1643                    attr: attr1,
1644                },
1645            )
1646            | (
1647                HasAttr { expr, attr },
1648                HasAttr {
1649                    expr: expr1,
1650                    attr: attr1,
1651                },
1652            ) => attr == attr1 && expr.eq_shape(expr1),
1653            (
1654                ExtHasAttr { expr, attrs },
1655                ExtHasAttr {
1656                    expr: expr1,
1657                    attrs: attrs1,
1658                },
1659            ) => attrs == attrs1 && expr.eq_shape(expr1),
1660            (
1661                Like { expr, pattern },
1662                Like {
1663                    expr: expr1,
1664                    pattern: pattern1,
1665                },
1666            ) => pattern == pattern1 && expr.eq_shape(expr1),
1667            (Set(elems), Set(elems1)) => {
1668                elems.len() == elems1.len()
1669                    && elems
1670                        .iter()
1671                        .zip(elems1.iter())
1672                        .all(|(e, e1)| e.eq_shape(e1))
1673            }
1674            (Record(map), Record(map1)) => {
1675                map.len() == map1.len()
1676                    && map
1677                        .iter()
1678                        .zip(map1.iter()) // relying on BTreeMap producing an iterator sorted by key
1679                        .all(|((a, e), (a1, e1))| a == a1 && e.eq_shape(e1))
1680            }
1681            (
1682                Is { expr, entity_type },
1683                Is {
1684                    expr: expr1,
1685                    entity_type: entity_type1,
1686                },
1687            ) => entity_type == entity_type1 && expr.eq_shape(expr1),
1688            _ => false,
1689        }
1690    }
1691
1692    /// Implementation of hashing corresponding to equality as implemented by
1693    /// `eq_shape`. Must satisfy the usual relationship between equality and
1694    /// hashing.
1695    pub fn hash_shape<H>(&self, state: &mut H)
1696    where
1697        H: Hasher,
1698    {
1699        mem::discriminant(self).hash(state);
1700        match self.expr_kind() {
1701            ExprKind::Lit(lit) => lit.hash(state),
1702            ExprKind::Var(v) => v.hash(state),
1703            ExprKind::Slot(s) => s.hash(state),
1704            ExprKind::Unknown(u) => u.hash(state),
1705            ExprKind::If {
1706                test_expr,
1707                then_expr,
1708                else_expr,
1709            } => {
1710                test_expr.hash_shape(state);
1711                then_expr.hash_shape(state);
1712                else_expr.hash_shape(state);
1713            }
1714            ExprKind::And { left, right } => {
1715                left.hash_shape(state);
1716                right.hash_shape(state);
1717            }
1718            ExprKind::Or { left, right } => {
1719                left.hash_shape(state);
1720                right.hash_shape(state);
1721            }
1722            ExprKind::UnaryApp { op, arg } => {
1723                op.hash(state);
1724                arg.hash_shape(state);
1725            }
1726            ExprKind::BinaryApp { op, arg1, arg2 } => {
1727                op.hash(state);
1728                arg1.hash_shape(state);
1729                arg2.hash_shape(state);
1730            }
1731            ExprKind::ExtensionFunctionApp { fn_name, args } => {
1732                fn_name.hash(state);
1733                state.write_usize(args.len());
1734                args.iter().for_each(|a| {
1735                    a.hash_shape(state);
1736                });
1737            }
1738            ExprKind::GetAttr { expr, attr } => {
1739                expr.hash_shape(state);
1740                attr.hash(state);
1741            }
1742            ExprKind::HasAttr { expr, attr } => {
1743                expr.hash_shape(state);
1744                attr.hash(state);
1745            }
1746            ExprKind::ExtHasAttr { expr, attrs } => {
1747                expr.hash_shape(state);
1748                attrs.hash(state);
1749            }
1750            ExprKind::Like { expr, pattern } => {
1751                expr.hash_shape(state);
1752                pattern.hash(state);
1753            }
1754            ExprKind::Set(elems) => {
1755                state.write_usize(elems.len());
1756                elems.iter().for_each(|e| {
1757                    e.hash_shape(state);
1758                })
1759            }
1760            ExprKind::Record(map) => {
1761                state.write_usize(map.len());
1762                map.iter().for_each(|(s, a)| {
1763                    s.hash(state);
1764                    a.hash_shape(state);
1765                });
1766            }
1767            ExprKind::Is { expr, entity_type } => {
1768                expr.hash_shape(state);
1769                entity_type.hash(state);
1770            }
1771            #[cfg(feature = "tolerant-ast")]
1772            ExprKind::Error { error_kind, .. } => error_kind.hash(state),
1773        }
1774    }
1775
1776    /// Implementation of ordering corresponding to equality as implemented by
1777    /// `eq_shape`. Must satisfy the usual relationship between equality and
1778    /// ordering.
1779    pub fn cmp_shape(&self, other: &Expr<T>) -> std::cmp::Ordering {
1780        // First compare variants for early short-circuiting using discriminant
1781        let self_kind = self.expr_kind();
1782        let other_kind = other.expr_kind();
1783        if std::mem::discriminant(self_kind) != std::mem::discriminant(other_kind) {
1784            return self_kind.variant_order().cmp(&other_kind.variant_order());
1785        }
1786
1787        // Same variants, compare contents
1788        use ExprKind::*;
1789        match (self_kind, other_kind) {
1790            (Lit(lit), Lit(lit1)) => lit.cmp(lit1),
1791            (Var(v), Var(v1)) => v.cmp(v1),
1792            (Slot(s), Slot(s1)) => s.cmp(s1),
1793            (
1794                Unknown(self::Unknown {
1795                    name: name1,
1796                    type_annotation: ta_1,
1797                }),
1798                Unknown(self::Unknown {
1799                    name: name2,
1800                    type_annotation: ta_2,
1801                }),
1802            ) => name1.cmp(name2).then_with(|| ta_1.cmp(ta_2)),
1803            (
1804                If {
1805                    test_expr,
1806                    then_expr,
1807                    else_expr,
1808                },
1809                If {
1810                    test_expr: test_expr1,
1811                    then_expr: then_expr1,
1812                    else_expr: else_expr1,
1813                },
1814            ) => test_expr
1815                .cmp_shape(test_expr1)
1816                .then_with(|| then_expr.cmp_shape(then_expr1))
1817                .then_with(|| else_expr.cmp_shape(else_expr1)),
1818            (
1819                And { left, right },
1820                And {
1821                    left: left1,
1822                    right: right1,
1823                },
1824            ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1825            (
1826                Or { left, right },
1827                Or {
1828                    left: left1,
1829                    right: right1,
1830                },
1831            ) => left.cmp_shape(left1).then_with(|| right.cmp_shape(right1)),
1832            (UnaryApp { op, arg }, UnaryApp { op: op1, arg: arg1 }) => {
1833                op.cmp(op1).then_with(|| arg.cmp_shape(arg1))
1834            }
1835            (
1836                BinaryApp { op, arg1, arg2 },
1837                BinaryApp {
1838                    op: op1,
1839                    arg1: arg11,
1840                    arg2: arg21,
1841                },
1842            ) => op
1843                .cmp(op1)
1844                .then_with(|| arg1.cmp_shape(arg11))
1845                .then_with(|| arg2.cmp_shape(arg21)),
1846            (
1847                ExtensionFunctionApp { fn_name, args },
1848                ExtensionFunctionApp {
1849                    fn_name: fn_name1,
1850                    args: args1,
1851                },
1852            ) => fn_name.cmp(fn_name1).then_with(|| {
1853                args.len().cmp(&args1.len()).then_with(|| {
1854                    for (a, a1) in args.iter().zip(args1.iter()) {
1855                        match a.cmp_shape(a1) {
1856                            std::cmp::Ordering::Equal => continue,
1857                            other => return other,
1858                        }
1859                    }
1860                    std::cmp::Ordering::Equal
1861                })
1862            }),
1863            (
1864                GetAttr { expr, attr },
1865                GetAttr {
1866                    expr: expr1,
1867                    attr: attr1,
1868                },
1869            ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1870            (
1871                HasAttr { expr, attr },
1872                HasAttr {
1873                    expr: expr1,
1874                    attr: attr1,
1875                },
1876            ) => attr.cmp(attr1).then_with(|| expr.cmp_shape(expr1)),
1877            (
1878                ExtHasAttr { expr, attrs },
1879                ExtHasAttr {
1880                    expr: expr1,
1881                    attrs: attrs1,
1882                },
1883            ) => attrs.cmp(attrs1).then_with(|| expr.cmp_shape(expr1)),
1884            (
1885                Like { expr, pattern },
1886                Like {
1887                    expr: expr1,
1888                    pattern: pattern1,
1889                },
1890            ) => pattern.cmp(pattern1).then_with(|| expr.cmp_shape(expr1)),
1891            (Set(elems), Set(elems1)) => elems.len().cmp(&elems1.len()).then_with(|| {
1892                for (e, e1) in elems.iter().zip(elems1.iter()) {
1893                    match e.cmp_shape(e1) {
1894                        std::cmp::Ordering::Equal => continue,
1895                        other => return other,
1896                    }
1897                }
1898                std::cmp::Ordering::Equal
1899            }),
1900            (Record(map), Record(map1)) => map.len().cmp(&map1.len()).then_with(|| {
1901                for ((a, e), (a1, e1)) in map.iter().zip(map1.iter()) {
1902                    match a.cmp(a1).then_with(|| e.cmp_shape(e1)) {
1903                        std::cmp::Ordering::Equal => continue,
1904                        other => return other,
1905                    }
1906                }
1907                std::cmp::Ordering::Equal
1908            }),
1909            (
1910                Is { expr, entity_type },
1911                Is {
1912                    expr: expr1,
1913                    entity_type: entity_type1,
1914                },
1915            ) => entity_type
1916                .cmp(entity_type1)
1917                .then_with(|| expr.cmp_shape(expr1)),
1918            #[cfg(feature = "tolerant-ast")]
1919            (
1920                Error { error_kind },
1921                Error {
1922                    error_kind: error_kind1,
1923                },
1924            ) => error_kind.cmp(error_kind1),
1925            #[expect(
1926                clippy::unreachable,
1927                reason = "This should never be reached since we compare variants first"
1928            )]
1929            _ => unreachable!(
1930                "Different variants should have been handled by variant_order comparison"
1931            ),
1932        }
1933    }
1934}
1935
1936/// AST variables
1937#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
1938#[serde(rename_all = "camelCase")]
1939#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1940#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
1941#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
1942pub enum Var {
1943    /// the Principal of the given request
1944    Principal,
1945    /// the Action of the given request
1946    Action,
1947    /// the Resource of the given request
1948    Resource,
1949    /// the Context of the given request
1950    Context,
1951}
1952
1953impl From<PrincipalOrResource> for Var {
1954    fn from(v: PrincipalOrResource) -> Self {
1955        match v {
1956            PrincipalOrResource::Principal => Var::Principal,
1957            PrincipalOrResource::Resource => Var::Resource,
1958        }
1959    }
1960}
1961
1962#[expect(
1963    clippy::fallible_impl_from,
1964    reason = "Tested by `test::all_vars_are_ids`. Never panics"
1965)]
1966impl From<Var> for Id {
1967    fn from(var: Var) -> Self {
1968        #[expect(
1969            clippy::unwrap_used,
1970            reason = "`Var` is a simple enum and all vars are formatted as valid `Id`. Tested by `test::all_vars_are_ids`"
1971        )]
1972        format!("{var}").parse().unwrap()
1973    }
1974}
1975
1976#[expect(
1977    clippy::fallible_impl_from,
1978    reason = "Tested by `test::all_vars_are_ids`. Never panics"
1979)]
1980impl From<Var> for UnreservedId {
1981    fn from(var: Var) -> Self {
1982        #[expect(
1983            clippy::unwrap_used,
1984            reason = "`Var` is a simple enum and all vars are formatted as valid `UnreservedId`. Tested by `test::all_vars_are_ids`"
1985        )]
1986        Id::from(var).try_into().unwrap()
1987    }
1988}
1989
1990impl std::fmt::Display for Var {
1991    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1992        match self {
1993            Self::Principal => write!(f, "principal"),
1994            Self::Action => write!(f, "action"),
1995            Self::Resource => write!(f, "resource"),
1996            Self::Context => write!(f, "context"),
1997        }
1998    }
1999}
2000
2001#[cfg(test)]
2002mod test {
2003    use cool_asserts::assert_matches;
2004    use itertools::Itertools;
2005    use smol_str::ToSmolStr;
2006    use std::collections::{hash_map::DefaultHasher, HashSet};
2007
2008    use super::*;
2009
2010    pub fn all_vars() -> impl Iterator<Item = Var> {
2011        [Var::Principal, Var::Action, Var::Resource, Var::Context].into_iter()
2012    }
2013
2014    // Tests that Var::Into never panics
2015    #[test]
2016    fn all_vars_are_ids() {
2017        for var in all_vars() {
2018            let _id: Id = var.into();
2019            let _id: UnreservedId = var.into();
2020        }
2021    }
2022
2023    #[test]
2024    fn exprs() {
2025        assert_eq!(
2026            Expr::val(33),
2027            Expr::new(ExprKind::Lit(Literal::Long(33)), None, ())
2028        );
2029        assert_eq!(
2030            Expr::val("hello"),
2031            Expr::new(ExprKind::Lit(Literal::from("hello")), None, ())
2032        );
2033        assert_eq!(
2034            Expr::val(EntityUID::with_eid("foo")),
2035            Expr::new(
2036                ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2037                None,
2038                ()
2039            )
2040        );
2041        assert_eq!(
2042            Expr::var(Var::Principal),
2043            Expr::new(ExprKind::Var(Var::Principal), None, ())
2044        );
2045        assert_eq!(
2046            Expr::ite(Expr::val(true), Expr::val(88), Expr::val(-100)),
2047            Expr::new(
2048                ExprKind::If {
2049                    test_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(true)), None, ())),
2050                    then_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(88)), None, ())),
2051                    else_expr: Arc::new(Expr::new(ExprKind::Lit(Literal::Long(-100)), None, ())),
2052                },
2053                None,
2054                ()
2055            )
2056        );
2057        assert_eq!(
2058            Expr::not(Expr::val(false)),
2059            Expr::new(
2060                ExprKind::UnaryApp {
2061                    op: UnaryOp::Not,
2062                    arg: Arc::new(Expr::new(ExprKind::Lit(Literal::Bool(false)), None, ())),
2063                },
2064                None,
2065                ()
2066            )
2067        );
2068        assert_eq!(
2069            Expr::get_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2070            Expr::new(
2071                ExprKind::GetAttr {
2072                    expr: Arc::new(Expr::new(
2073                        ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2074                        None,
2075                        ()
2076                    )),
2077                    attr: "some_attr".into()
2078                },
2079                None,
2080                ()
2081            )
2082        );
2083        assert_eq!(
2084            Expr::has_attr(Expr::val(EntityUID::with_eid("foo")), "some_attr".into()),
2085            Expr::new(
2086                ExprKind::HasAttr {
2087                    expr: Arc::new(Expr::new(
2088                        ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2089                        None,
2090                        ()
2091                    )),
2092                    attr: "some_attr".into()
2093                },
2094                None,
2095                ()
2096            )
2097        );
2098        assert_eq!(
2099            Expr::is_entity_type(
2100                Expr::val(EntityUID::with_eid("foo")),
2101                "Type".parse().unwrap()
2102            ),
2103            Expr::new(
2104                ExprKind::Is {
2105                    expr: Arc::new(Expr::new(
2106                        ExprKind::Lit(Literal::from(EntityUID::with_eid("foo"))),
2107                        None,
2108                        ()
2109                    )),
2110                    entity_type: "Type".parse().unwrap()
2111                },
2112                None,
2113                ()
2114            ),
2115        );
2116    }
2117
2118    #[test]
2119    fn like_display() {
2120        // `\0` escaped form is `\0`.
2121        let e = Expr::like(Expr::val("a"), Pattern::from(vec![PatternElem::Char('\0')]));
2122        assert_eq!(format!("{e}"), r#""a" like "\0""#);
2123        // `\`'s escaped form is `\\`
2124        let e = Expr::like(
2125            Expr::val("a"),
2126            Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('0')]),
2127        );
2128        assert_eq!(format!("{e}"), r#""a" like "\\0""#);
2129        // `\`'s escaped form is `\\`
2130        let e = Expr::like(
2131            Expr::val("a"),
2132            Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Wildcard]),
2133        );
2134        assert_eq!(format!("{e}"), r#""a" like "\\*""#);
2135        // literal star's escaped from is `\*`
2136        let e = Expr::like(
2137            Expr::val("a"),
2138            Pattern::from(vec![PatternElem::Char('\\'), PatternElem::Char('*')]),
2139        );
2140        assert_eq!(format!("{e}"), r#""a" like "\\\*""#);
2141    }
2142
2143    #[test]
2144    fn has_display() {
2145        // `\0` escaped form is `\0`.
2146        let e = Expr::has_attr(Expr::val("a"), "\0".into());
2147        assert_eq!(format!("{e}"), r#""a" has "\0""#);
2148        // `\`'s escaped form is `\\`
2149        let e = Expr::has_attr(Expr::val("a"), r"\".into());
2150        assert_eq!(format!("{e}"), r#""a" has "\\""#);
2151    }
2152
2153    #[test]
2154    fn extended_has_display() {
2155        use nonempty::nonempty;
2156        // Extended has with 2 attributes
2157        let e =
2158            Expr::extended_has_attr(Expr::var(Var::Principal), nonempty!["a".into(), "b".into()]);
2159        assert_eq!(format!("{e}"), "principal has a.b");
2160        // Extended has with 3 attributes
2161        let e = Expr::extended_has_attr(
2162            Expr::var(Var::Context),
2163            nonempty!["user".into(), "profile".into(), "email".into()],
2164        );
2165        assert_eq!(format!("{e}"), "context has user.profile.email");
2166        // Extended has preserves structure through display roundtrip
2167        let e = Expr::extended_has_attr(
2168            Expr::var(Var::Resource),
2169            nonempty!["owner".into(), "ipinfo".into(), "additionalData".into()],
2170        );
2171        let displayed = format!("{e}");
2172        assert_eq!(displayed, "resource has owner.ipinfo.additionalData");
2173        let reparsed = displayed.parse::<Expr>().unwrap();
2174        assert!(e.eq_shape(&reparsed));
2175    }
2176
2177    #[test]
2178    fn slot_display() {
2179        let e = Expr::slot(SlotId::principal());
2180        assert_eq!(format!("{e}"), "?principal");
2181        let e = Expr::slot(SlotId::resource());
2182        assert_eq!(format!("{e}"), "?resource");
2183        let e = Expr::val(EntityUID::with_eid("eid"));
2184        assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
2185    }
2186
2187    #[test]
2188    fn simple_slots() {
2189        let e = Expr::slot(SlotId::principal());
2190        let p = SlotId::principal();
2191        let r = SlotId::resource();
2192        let set: HashSet<SlotId> = HashSet::from_iter([p]);
2193        assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2194        let e = Expr::or(
2195            Expr::slot(SlotId::principal()),
2196            Expr::ite(
2197                Expr::val(true),
2198                Expr::slot(SlotId::resource()),
2199                Expr::val(false),
2200            ),
2201        );
2202        let set: HashSet<SlotId> = HashSet::from_iter([p, r]);
2203        assert_eq!(set, e.slots().map(|slot| slot.id).collect::<HashSet<_>>());
2204    }
2205
2206    #[test]
2207    fn unknowns() {
2208        let e = Expr::ite(
2209            Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2210            Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2211            Expr::unknown(Unknown::new_untyped("c")),
2212        );
2213        let unknowns = e.unknowns().collect_vec();
2214        assert_eq!(unknowns.len(), 3);
2215        assert!(unknowns.contains(&&Unknown::new_untyped("a")));
2216        assert!(unknowns.contains(&&Unknown::new_untyped("b")));
2217        assert!(unknowns.contains(&&Unknown::new_untyped("c")));
2218    }
2219
2220    #[test]
2221    fn is_unknown() {
2222        let e = Expr::ite(
2223            Expr::not(Expr::unknown(Unknown::new_untyped("a"))),
2224            Expr::and(Expr::unknown(Unknown::new_untyped("b")), Expr::val(3)),
2225            Expr::unknown(Unknown::new_untyped("c")),
2226        );
2227        assert!(e.contains_unknown());
2228        let e = Expr::ite(
2229            Expr::not(Expr::val(true)),
2230            Expr::and(Expr::val(1), Expr::val(3)),
2231            Expr::val(1),
2232        );
2233        assert!(!e.contains_unknown());
2234    }
2235
2236    #[test]
2237    fn expr_with_data() {
2238        let e = ExprBuilder::with_data("data").val(1);
2239        assert_eq!(e.into_data(), "data");
2240    }
2241
2242    #[test]
2243    fn expr_shape_only_eq() {
2244        let temp = ExprBuilder::with_data(1).val(1);
2245        let exprs = &[
2246            (ExprBuilder::with_data(1).val(33), Expr::val(33)),
2247            (ExprBuilder::with_data(1).val(true), Expr::val(true)),
2248            (
2249                ExprBuilder::with_data(1).var(Var::Principal),
2250                Expr::var(Var::Principal),
2251            ),
2252            (
2253                ExprBuilder::with_data(1).slot(SlotId::principal()),
2254                Expr::slot(SlotId::principal()),
2255            ),
2256            (
2257                ExprBuilder::with_data(1).ite(temp.clone(), temp.clone(), temp.clone()),
2258                Expr::ite(Expr::val(1), Expr::val(1), Expr::val(1)),
2259            ),
2260            (
2261                ExprBuilder::with_data(1).not(temp.clone()),
2262                Expr::not(Expr::val(1)),
2263            ),
2264            (
2265                ExprBuilder::with_data(1).is_eq(temp.clone(), temp.clone()),
2266                Expr::is_eq(Expr::val(1), Expr::val(1)),
2267            ),
2268            (
2269                ExprBuilder::with_data(1).and(temp.clone(), temp.clone()),
2270                Expr::and(Expr::val(1), Expr::val(1)),
2271            ),
2272            (
2273                ExprBuilder::with_data(1).or(temp.clone(), temp.clone()),
2274                Expr::or(Expr::val(1), Expr::val(1)),
2275            ),
2276            (
2277                ExprBuilder::with_data(1).less(temp.clone(), temp.clone()),
2278                Expr::less(Expr::val(1), Expr::val(1)),
2279            ),
2280            (
2281                ExprBuilder::with_data(1).lesseq(temp.clone(), temp.clone()),
2282                Expr::lesseq(Expr::val(1), Expr::val(1)),
2283            ),
2284            (
2285                ExprBuilder::with_data(1).greater(temp.clone(), temp.clone()),
2286                Expr::greater(Expr::val(1), Expr::val(1)),
2287            ),
2288            (
2289                ExprBuilder::with_data(1).greatereq(temp.clone(), temp.clone()),
2290                Expr::greatereq(Expr::val(1), Expr::val(1)),
2291            ),
2292            (
2293                ExprBuilder::with_data(1).add(temp.clone(), temp.clone()),
2294                Expr::add(Expr::val(1), Expr::val(1)),
2295            ),
2296            (
2297                ExprBuilder::with_data(1).sub(temp.clone(), temp.clone()),
2298                Expr::sub(Expr::val(1), Expr::val(1)),
2299            ),
2300            (
2301                ExprBuilder::with_data(1).mul(temp.clone(), temp.clone()),
2302                Expr::mul(Expr::val(1), Expr::val(1)),
2303            ),
2304            (
2305                ExprBuilder::with_data(1).neg(temp.clone()),
2306                Expr::neg(Expr::val(1)),
2307            ),
2308            (
2309                ExprBuilder::with_data(1).is_in(temp.clone(), temp.clone()),
2310                Expr::is_in(Expr::val(1), Expr::val(1)),
2311            ),
2312            (
2313                ExprBuilder::with_data(1).contains(temp.clone(), temp.clone()),
2314                Expr::contains(Expr::val(1), Expr::val(1)),
2315            ),
2316            (
2317                ExprBuilder::with_data(1).contains_all(temp.clone(), temp.clone()),
2318                Expr::contains_all(Expr::val(1), Expr::val(1)),
2319            ),
2320            (
2321                ExprBuilder::with_data(1).contains_any(temp.clone(), temp.clone()),
2322                Expr::contains_any(Expr::val(1), Expr::val(1)),
2323            ),
2324            (
2325                ExprBuilder::with_data(1).is_empty(temp.clone()),
2326                Expr::is_empty(Expr::val(1)),
2327            ),
2328            (
2329                ExprBuilder::with_data(1).set([temp.clone()]),
2330                Expr::set([Expr::val(1)]),
2331            ),
2332            (
2333                ExprBuilder::with_data(1)
2334                    .record([("foo".into(), temp.clone())])
2335                    .unwrap(),
2336                Expr::record([("foo".into(), Expr::val(1))]).unwrap(),
2337            ),
2338            (
2339                ExprBuilder::with_data(1)
2340                    .call_extension_fn("foo".parse().unwrap(), vec![temp.clone()])
2341                    .unwrap_infallible(),
2342                Expr::call_extension_fn("foo".parse().unwrap(), vec![Expr::val(1)]),
2343            ),
2344            (
2345                ExprBuilder::with_data(1).get_attr(temp.clone(), "foo".into()),
2346                Expr::get_attr(Expr::val(1), "foo".into()),
2347            ),
2348            (
2349                ExprBuilder::with_data(1).has_attr(temp.clone(), "foo".into()),
2350                Expr::has_attr(Expr::val(1), "foo".into()),
2351            ),
2352            (
2353                ExprBuilder::with_data(1)
2354                    .like(temp.clone(), Pattern::from(vec![PatternElem::Wildcard])),
2355                Expr::like(Expr::val(1), Pattern::from(vec![PatternElem::Wildcard])),
2356            ),
2357            (
2358                ExprBuilder::with_data(1).is_entity_type(temp, "T".parse().unwrap()),
2359                Expr::is_entity_type(Expr::val(1), "T".parse().unwrap()),
2360            ),
2361        ];
2362
2363        for (e0, e1) in exprs {
2364            assert!(e0.eq_shape(e0));
2365            assert!(e1.eq_shape(e1));
2366            assert!(e0.eq_shape(e1));
2367            assert!(e1.eq_shape(e0));
2368
2369            let mut hasher0 = DefaultHasher::new();
2370            e0.hash_shape(&mut hasher0);
2371            let hash0 = hasher0.finish();
2372
2373            let mut hasher1 = DefaultHasher::new();
2374            e1.hash_shape(&mut hasher1);
2375            let hash1 = hasher1.finish();
2376
2377            assert_eq!(hash0, hash1);
2378        }
2379    }
2380
2381    #[test]
2382    fn expr_shape_only_not_eq() {
2383        let expr1 = ExprBuilder::with_data(1).val(1);
2384        let expr2 = ExprBuilder::with_data(1).val(2);
2385        assert_ne!(
2386            ExprShapeOnly::new_from_borrowed(&expr1),
2387            ExprShapeOnly::new_from_borrowed(&expr2)
2388        );
2389    }
2390
2391    #[test]
2392    fn expr_shape_only_set_prefix_ne() {
2393        let e1 = ExprShapeOnly::new_from_owned(Expr::set([]));
2394        let e2 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1)]));
2395        let e3 = ExprShapeOnly::new_from_owned(Expr::set([Expr::val(1), Expr::val(2)]));
2396
2397        assert_ne!(e1, e2);
2398        assert_ne!(e1, e3);
2399        assert_ne!(e2, e1);
2400        assert_ne!(e2, e3);
2401        assert_ne!(e3, e1);
2402        assert_ne!(e2, e1);
2403    }
2404
2405    #[test]
2406    fn expr_shape_only_ext_fn_arg_prefix_ne() {
2407        let e1 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2408            "decimal".parse().unwrap(),
2409            vec![],
2410        ));
2411        let e2 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2412            "decimal".parse().unwrap(),
2413            vec![Expr::val("0.0")],
2414        ));
2415        let e3 = ExprShapeOnly::new_from_owned(Expr::call_extension_fn(
2416            "decimal".parse().unwrap(),
2417            vec![Expr::val("0.0"), Expr::val("0.0")],
2418        ));
2419
2420        assert_ne!(e1, e2);
2421        assert_ne!(e1, e3);
2422        assert_ne!(e2, e1);
2423        assert_ne!(e2, e3);
2424        assert_ne!(e3, e1);
2425        assert_ne!(e2, e1);
2426    }
2427
2428    #[test]
2429    fn expr_shape_only_record_attr_prefix_ne() {
2430        let e1 = ExprShapeOnly::new_from_owned(Expr::record([]).unwrap());
2431        let e2 = ExprShapeOnly::new_from_owned(
2432            Expr::record([("a".to_smolstr(), Expr::val(1))]).unwrap(),
2433        );
2434        let e3 = ExprShapeOnly::new_from_owned(
2435            Expr::record([
2436                ("a".to_smolstr(), Expr::val(1)),
2437                ("b".to_smolstr(), Expr::val(2)),
2438            ])
2439            .unwrap(),
2440        );
2441
2442        assert_ne!(e1, e2);
2443        assert_ne!(e1, e3);
2444        assert_ne!(e2, e1);
2445        assert_ne!(e2, e3);
2446        assert_ne!(e3, e1);
2447        assert_ne!(e2, e1);
2448    }
2449
2450    #[test]
2451    fn untyped_subst_present() {
2452        let u = Unknown {
2453            name: "foo".into(),
2454            type_annotation: None,
2455        };
2456        let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2457        match r {
2458            Ok(e) => assert_eq!(e, Expr::val(1)),
2459            Err(empty) => match empty {},
2460        }
2461    }
2462
2463    #[test]
2464    fn untyped_subst_present_correct_type() {
2465        let u = Unknown {
2466            name: "foo".into(),
2467            type_annotation: Some(Type::Long),
2468        };
2469        let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2470        match r {
2471            Ok(e) => assert_eq!(e, Expr::val(1)),
2472            Err(empty) => match empty {},
2473        }
2474    }
2475
2476    #[test]
2477    fn untyped_subst_present_wrong_type() {
2478        let u = Unknown {
2479            name: "foo".into(),
2480            type_annotation: Some(Type::Bool),
2481        };
2482        let r = UntypedSubstitution::substitute(&u, Some(&Value::new(1, None)));
2483        match r {
2484            Ok(e) => assert_eq!(e, Expr::val(1)),
2485            Err(empty) => match empty {},
2486        }
2487    }
2488
2489    #[test]
2490    fn untyped_subst_not_present() {
2491        let u = Unknown {
2492            name: "foo".into(),
2493            type_annotation: Some(Type::Bool),
2494        };
2495        let r = UntypedSubstitution::substitute(&u, None);
2496        match r {
2497            Ok(n) => assert_eq!(n, Expr::unknown(u)),
2498            Err(empty) => match empty {},
2499        }
2500    }
2501
2502    #[test]
2503    fn typed_subst_present() {
2504        let u = Unknown {
2505            name: "foo".into(),
2506            type_annotation: None,
2507        };
2508        let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2509        assert_eq!(e, Expr::val(1));
2510    }
2511
2512    #[test]
2513    fn typed_subst_present_correct_type() {
2514        let u = Unknown {
2515            name: "foo".into(),
2516            type_annotation: Some(Type::Long),
2517        };
2518        let e = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap();
2519        assert_eq!(e, Expr::val(1));
2520    }
2521
2522    #[test]
2523    fn typed_subst_present_wrong_type() {
2524        let u = Unknown {
2525            name: "foo".into(),
2526            type_annotation: Some(Type::Bool),
2527        };
2528        let r = TypedSubstitution::substitute(&u, Some(&Value::new(1, None))).unwrap_err();
2529        assert_matches!(
2530            r,
2531            SubstitutionError::TypeError {
2532                expected: Type::Bool,
2533                actual: Type::Long,
2534            }
2535        );
2536    }
2537
2538    #[test]
2539    fn typed_subst_not_present() {
2540        let u = Unknown {
2541            name: "foo".into(),
2542            type_annotation: None,
2543        };
2544        let r = TypedSubstitution::substitute(&u, None).unwrap();
2545        assert_eq!(r, Expr::unknown(u));
2546    }
2547}
2548
2549#[cfg(test)]
2550mod validate_test {
2551    use cool_asserts::assert_matches;
2552
2553    use super::*;
2554
2555    fn ext_call(name: &str, args: Vec<Expr>) -> Expr {
2556        Expr::call_extension_fn(Name::parse_unqualified_name(name).unwrap(), args)
2557    }
2558
2559    #[test]
2560    fn valid_function_style_accepted() {
2561        assert!(ext_call("ip", vec![Expr::val("127.0.0.1")])
2562            .try_validate()
2563            .is_ok());
2564    }
2565
2566    #[test]
2567    fn valid_method_style_accepted() {
2568        let receiver = ext_call("ip", vec![Expr::val("127.0.0.1")]);
2569        assert!(ext_call("isIpv4", vec![receiver]).try_validate().is_ok());
2570    }
2571
2572    #[test]
2573    fn unknown_extension_fn_rejected() {
2574        let err = ext_call("notReal", vec![Expr::val("x")])
2575            .try_validate()
2576            .unwrap_err();
2577        assert!(
2578            err.to_string().contains("unknown extension function"),
2579            "got: {err}"
2580        );
2581    }
2582
2583    #[test]
2584    fn method_style_empty_args_rejected() {
2585        let err = ext_call("isIpv4", vec![]).try_validate().unwrap_err();
2586        assert!(
2587            err.to_string().contains("requires a receiver argument"),
2588            "got: {err}"
2589        );
2590    }
2591
2592    #[test]
2593    fn extended_has_with_invalid_ids_rejected() {
2594        let exprs = vec![
2595            Expr::extended_has_attr(
2596                Expr::var(Var::Principal),
2597                nonempty::nonempty!["".into(), "a".into()], // principal has "".a
2598            ),
2599            Expr::extended_has_attr(
2600                Expr::var(Var::Principal),
2601                nonempty::nonempty!["a".into(), "".into()], // principal has a.""
2602            ),
2603            Expr::extended_has_attr(
2604                Expr::var(Var::Principal),
2605                nonempty::nonempty!["true".into(), "a".into()], // principal has true.a
2606            ),
2607        ];
2608        for e in exprs {
2609            let e = e.try_validate();
2610            assert_matches!(e, Err(ExprValidationError(..)));
2611            assert!(e
2612                .unwrap_err()
2613                .to_string()
2614                .starts_with("invalid expression: extended has attribute"))
2615        }
2616    }
2617}