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