cedar_policy_core/evaluator/
err.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use crate::ast::*;
18use crate::extensions::ExtensionFunctionLookupError;
19use crate::parser::Loc;
20use miette::Diagnostic;
21use nonempty::{nonempty, NonEmpty};
22use smol_str::SmolStr;
23use std::sync::Arc;
24use thiserror::Error;
25
26// How many attrs or tags will we store in an error before cutting off for performance reason
27const TOO_MANY_ATTRS: usize = 5;
28
29/// Enumeration of the possible errors that can occur during evaluation
30//
31// CAUTION: this type is publicly exported in `cedar-policy`.
32// Don't make fields `pub`, don't make breaking changes, and use caution when
33// adding public methods.
34#[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
35pub enum EvaluationError {
36    /// Tried to lookup an entity UID, but it didn't exist in the provided
37    /// entities
38    #[error(transparent)]
39    #[diagnostic(transparent)]
40    EntityDoesNotExist(#[from] evaluation_errors::EntityDoesNotExistError),
41
42    /// Tried to get an attribute or tag, but the specified entity didn't
43    /// have that attribute or tag
44    #[error(transparent)]
45    #[diagnostic(transparent)]
46    EntityAttrDoesNotExist(#[from] evaluation_errors::EntityAttrDoesNotExistError),
47
48    /// Tried to get an attribute of a (non-entity) record, but that record
49    /// didn't have that attribute
50    #[error(transparent)]
51    #[diagnostic(transparent)]
52    RecordAttrDoesNotExist(#[from] evaluation_errors::RecordAttrDoesNotExistError),
53
54    /// An error occurred when looking up an extension function
55    #[error(transparent)]
56    #[diagnostic(transparent)]
57    FailedExtensionFunctionLookup(#[from] ExtensionFunctionLookupError),
58
59    /// Tried to evaluate an operation on values with incorrect types for that
60    /// operation
61    #[error(transparent)]
62    #[diagnostic(transparent)]
63    TypeError(#[from] evaluation_errors::TypeError),
64
65    /// Wrong number of arguments provided to an extension function
66    #[error(transparent)]
67    #[diagnostic(transparent)]
68    WrongNumArguments(#[from] evaluation_errors::WrongNumArgumentsError),
69
70    /// Overflow during an integer operation
71    #[error(transparent)]
72    #[diagnostic(transparent)]
73    IntegerOverflow(#[from] evaluation_errors::IntegerOverflowError),
74
75    /// Not all template slots were linked
76    #[error(transparent)]
77    #[diagnostic(transparent)]
78    UnlinkedSlot(#[from] evaluation_errors::UnlinkedSlotError),
79
80    /// Evaluation error thrown by an extension function
81    #[error(transparent)]
82    #[diagnostic(transparent)]
83    FailedExtensionFunctionExecution(#[from] evaluation_errors::ExtensionFunctionExecutionError),
84
85    /// This error is raised if an expression contains unknowns and cannot be
86    /// reduced to a [`Value`]. In order to return partial results, use the
87    /// partial evaluation APIs instead.
88    #[error(transparent)]
89    #[diagnostic(transparent)]
90    NonValue(#[from] evaluation_errors::NonValueError),
91
92    /// Maximum recursion limit reached for expression evaluation
93    #[error(transparent)]
94    #[diagnostic(transparent)]
95    RecursionLimit(#[from] evaluation_errors::RecursionLimitError),
96}
97
98impl EvaluationError {
99    /// Extract the source location of the error, if one is attached
100    pub(crate) fn source_loc(&self) -> Option<&Loc> {
101        match self {
102            Self::EntityDoesNotExist(e) => e.source_loc.as_ref(),
103            Self::EntityAttrDoesNotExist(e) => e.source_loc.as_ref(),
104            Self::RecordAttrDoesNotExist(e) => e.source_loc.as_ref(),
105            Self::FailedExtensionFunctionLookup(e) => e.source_loc(),
106            Self::TypeError(e) => e.source_loc.as_ref(),
107            Self::WrongNumArguments(e) => e.source_loc.as_ref(),
108            Self::IntegerOverflow(e) => e.source_loc(),
109            Self::UnlinkedSlot(e) => e.source_loc.as_ref(),
110            Self::FailedExtensionFunctionExecution(e) => e.source_loc.as_ref(),
111            Self::NonValue(e) => e.source_loc.as_ref(),
112            Self::RecursionLimit(e) => e.source_loc.as_ref(),
113        }
114    }
115
116    /// Return the `EvaluationError`, but with the new `source_loc` (or `None`).
117    pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
118        match self {
119            Self::EntityDoesNotExist(e) => {
120                Self::EntityDoesNotExist(evaluation_errors::EntityDoesNotExistError {
121                    source_loc,
122                    ..e
123                })
124            }
125            Self::EntityAttrDoesNotExist(e) => {
126                Self::EntityAttrDoesNotExist(evaluation_errors::EntityAttrDoesNotExistError {
127                    source_loc,
128                    ..e
129                })
130            }
131            Self::RecordAttrDoesNotExist(e) => {
132                Self::RecordAttrDoesNotExist(evaluation_errors::RecordAttrDoesNotExistError {
133                    source_loc,
134                    ..e
135                })
136            }
137            Self::FailedExtensionFunctionLookup(e) => {
138                Self::FailedExtensionFunctionLookup(e.with_maybe_source_loc(source_loc))
139            }
140            Self::TypeError(e) => Self::TypeError(evaluation_errors::TypeError { source_loc, ..e }),
141            Self::WrongNumArguments(e) => {
142                Self::WrongNumArguments(evaluation_errors::WrongNumArgumentsError {
143                    source_loc,
144                    ..e
145                })
146            }
147            Self::IntegerOverflow(e) => Self::IntegerOverflow(e.with_maybe_source_loc(source_loc)),
148            Self::UnlinkedSlot(e) => {
149                Self::UnlinkedSlot(evaluation_errors::UnlinkedSlotError { source_loc, ..e })
150            }
151            Self::FailedExtensionFunctionExecution(e) => Self::FailedExtensionFunctionExecution(
152                evaluation_errors::ExtensionFunctionExecutionError { source_loc, ..e },
153            ),
154            Self::NonValue(e) => {
155                Self::NonValue(evaluation_errors::NonValueError { source_loc, ..e })
156            }
157            Self::RecursionLimit(_) => {
158                Self::RecursionLimit(evaluation_errors::RecursionLimitError { source_loc })
159            }
160        }
161    }
162
163    /// Construct a [`EntityDoesNotExist`] error
164    pub(crate) fn entity_does_not_exist(uid: Arc<EntityUID>, source_loc: Option<Loc>) -> Self {
165        evaluation_errors::EntityDoesNotExistError { uid, source_loc }.into()
166    }
167
168    /// Construct a [`EntityAttrDoesNotExist`] error
169    ///
170    /// `does_attr_exist_as_a_tag`: does `attr` exist on `entity` as a tag (rather than an attribute)
171    pub(crate) fn entity_attr_does_not_exist<'a>(
172        entity: Arc<EntityUID>,
173        attr: SmolStr,
174        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
175        does_attr_exist_as_a_tag: bool,
176        total_attrs: usize,
177        source_loc: Option<Loc>,
178    ) -> Self {
179        evaluation_errors::EntityAttrDoesNotExistError {
180            entity,
181            attr_or_tag: attr,
182            was_attr: true,
183            exists_the_other_kind: does_attr_exist_as_a_tag,
184            available_attrs_or_tags: available_attrs
185                .into_iter()
186                .take(TOO_MANY_ATTRS)
187                .cloned()
188                .collect::<Vec<_>>(),
189            total_attrs_or_tags: total_attrs,
190            source_loc,
191        }
192        .into()
193    }
194
195    /// Construct an error for the case where an entity tag does not exist
196    ///
197    /// `does_tag_exist_as_an_attr`: does `tag` exist on `entity` as an attribute (rather than a tag)
198    pub(crate) fn entity_tag_does_not_exist<'a>(
199        entity: Arc<EntityUID>,
200        tag: SmolStr,
201        available_tags: impl IntoIterator<Item = &'a SmolStr>,
202        does_tag_exist_as_an_attr: bool,
203        total_tags: usize,
204        source_loc: Option<Loc>,
205    ) -> Self {
206        evaluation_errors::EntityAttrDoesNotExistError {
207            entity,
208            attr_or_tag: tag,
209            was_attr: false,
210            exists_the_other_kind: does_tag_exist_as_an_attr,
211            available_attrs_or_tags: available_tags
212                .into_iter()
213                .take(TOO_MANY_ATTRS)
214                .cloned()
215                .collect::<Vec<_>>(),
216            total_attrs_or_tags: total_tags,
217            source_loc,
218        }
219        .into()
220    }
221
222    /// Construct a [`RecordAttrDoesNotExist`] error
223    pub(crate) fn record_attr_does_not_exist<'a>(
224        attr: SmolStr,
225        available_attrs: impl IntoIterator<Item = &'a SmolStr>,
226        total_attrs: usize,
227        source_loc: Option<Loc>,
228    ) -> Self {
229        evaluation_errors::RecordAttrDoesNotExistError {
230            attr,
231            available_attrs: available_attrs
232                .into_iter()
233                .take(TOO_MANY_ATTRS)
234                .cloned()
235                .collect(),
236            total_attrs,
237            source_loc,
238        }
239        .into()
240    }
241
242    /// Construct a [`TypeError`] error
243    pub(crate) fn type_error(expected: NonEmpty<Type>, actual: &Value) -> Self {
244        evaluation_errors::TypeError {
245            expected,
246            actual: actual.type_of(),
247            advice: None,
248            source_loc: actual.source_loc().cloned(),
249        }
250        .into()
251    }
252
253    pub(crate) fn type_error_single(expected: Type, actual: &Value) -> Self {
254        Self::type_error(nonempty![expected], actual)
255    }
256
257    /// Construct a [`TypeError`] error with the advice field set
258    pub(crate) fn type_error_with_advice(
259        expected: NonEmpty<Type>,
260        actual: &Value,
261        advice: String,
262    ) -> Self {
263        evaluation_errors::TypeError {
264            expected,
265            actual: actual.type_of(),
266            advice: Some(advice),
267            source_loc: actual.source_loc().cloned(),
268        }
269        .into()
270    }
271
272    pub(crate) fn type_error_with_advice_single(
273        expected: Type,
274        actual: &Value,
275        advice: String,
276    ) -> Self {
277        Self::type_error_with_advice(nonempty![expected], actual, advice)
278    }
279
280    /// Construct a [`WrongNumArguments`] error
281    pub(crate) fn wrong_num_arguments(
282        function_name: Name,
283        expected: usize,
284        actual: usize,
285        source_loc: Option<Loc>,
286    ) -> Self {
287        evaluation_errors::WrongNumArgumentsError {
288            function_name,
289            expected,
290            actual,
291            source_loc,
292        }
293        .into()
294    }
295
296    /// Construct a [`UnlinkedSlot`] error
297    pub(crate) fn unlinked_slot(slot: SlotId, source_loc: Option<Loc>) -> Self {
298        evaluation_errors::UnlinkedSlotError { slot, source_loc }.into()
299    }
300
301    /// Construct a [`FailedExtensionFunctionApplication`] error
302    pub(crate) fn failed_extension_function_application(
303        extension_name: Name,
304        msg: String,
305        source_loc: Option<Loc>,
306    ) -> Self {
307        evaluation_errors::ExtensionFunctionExecutionError {
308            extension_name,
309            msg,
310            source_loc,
311        }
312        .into()
313    }
314
315    /// Construct a [`NonValue`] error
316    pub(crate) fn non_value(expr: Expr) -> Self {
317        let source_loc = expr.source_loc().cloned();
318        evaluation_errors::NonValueError { expr, source_loc }.into()
319    }
320
321    /// Construct a [`RecursionLimit`] error
322    #[cfg(not(target_arch = "wasm32"))]
323    pub(crate) fn recursion_limit(source_loc: Option<Loc>) -> Self {
324        evaluation_errors::RecursionLimitError { source_loc }.into()
325    }
326}
327
328/// Error subtypes for [`EvaluationError`]
329pub mod evaluation_errors {
330    use crate::ast::{BinaryOp, EntityUID, Expr, SlotId, Type, UnaryOp, Value};
331    use crate::parser::Loc;
332    use itertools::Itertools;
333    use miette::Diagnostic;
334    use nonempty::NonEmpty;
335    use smol_str::SmolStr;
336    use std::sync::Arc;
337    use thiserror::Error;
338
339    use super::Name;
340
341    /// Tried to lookup an entity UID, but it didn't exist in the provided entities
342    //
343    // CAUTION: this type is publicly exported in `cedar-policy`.
344    // Don't make fields `pub`, don't make breaking changes, and use caution
345    // when adding public methods.
346    #[derive(Debug, PartialEq, Eq, Clone, Error)]
347    #[error("entity `{uid}` does not exist")]
348    pub struct EntityDoesNotExistError {
349        /// Entity UID which didn't exist in the provided entities
350        pub(crate) uid: Arc<EntityUID>,
351        /// Source location
352        pub(crate) source_loc: Option<Loc>,
353    }
354
355    // This and similar `Diagnostic` impls could just be derived with
356    //
357    // #[source_code]
358    // #[label]
359    // source_loc: Option<Loc>,
360    //
361    // if [miette#377](https://github.com/zkat/miette/issues/377) gets fixed.
362    // Or, we could have separate fields for source code and label instead of
363    // combining them into `Loc`, which would work around the issue.
364    impl Diagnostic for EntityDoesNotExistError {
365        impl_diagnostic_from_source_loc_opt_field!(source_loc);
366    }
367
368    /// Tried to get an attribute, but the specified entity didn't have that
369    /// attribute
370    //
371    // CAUTION: this type is publicly exported in `cedar-policy`.
372    // Don't make fields `pub`, don't make breaking changes, and use caution
373    // when adding public methods.
374    #[derive(Debug, PartialEq, Eq, Clone, Error)]
375    #[error("`{entity}` does not have the {} `{attr_or_tag}`", if *.was_attr { "attribute" } else { "tag" })]
376    pub struct EntityAttrDoesNotExistError {
377        /// Entity that didn't have the attribute
378        pub(crate) entity: Arc<EntityUID>,
379        /// Name of the attribute or tag it didn't have
380        pub(crate) attr_or_tag: SmolStr,
381        /// Whether this was an attempted attribute access (`true`) or tag access (`false`)
382        pub(crate) was_attr: bool,
383        /// If `true`, this is a case where we tried accessing an attribute but
384        /// there's a tag of that name, or vice versa
385        pub(crate) exists_the_other_kind: bool,
386        /// (First five) Available attributes/tags on the entity, depending on `was_attr`
387        pub(crate) available_attrs_or_tags: Vec<SmolStr>,
388        /// Total number of attributes/tags on the entity, depending on `was_attr`
389        pub(crate) total_attrs_or_tags: usize,
390        /// Source location
391        pub(crate) source_loc: Option<Loc>,
392    }
393
394    impl Diagnostic for EntityAttrDoesNotExistError {
395        impl_diagnostic_from_source_loc_opt_field!(source_loc);
396
397        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
398            let mut help_text = if self.available_attrs_or_tags.is_empty() {
399                format!(
400                    "`{}` does not have any {}",
401                    &self.entity,
402                    if self.was_attr { "attributes" } else { "tags" }
403                )
404            } else if self.available_attrs_or_tags.len() == self.total_attrs_or_tags {
405                format!(
406                    "available {}: [{}]",
407                    if self.was_attr { "attributes" } else { "tags" },
408                    self.available_attrs_or_tags.iter().join(",")
409                )
410            } else {
411                format!(
412                    "available {}: [{}, ... ({} more attributes) ]",
413                    if self.was_attr { "attributes" } else { "tags" },
414                    self.available_attrs_or_tags.iter().join(","),
415                    self.total_attrs_or_tags - self.available_attrs_or_tags.len()
416                )
417            };
418            if self.exists_the_other_kind {
419                help_text.push_str(&format!(
420                    "; note that {} (not {}) named `{}` does exist",
421                    if self.was_attr {
422                        "a tag"
423                    } else {
424                        "an attribute"
425                    },
426                    if self.was_attr {
427                        "an attribute"
428                    } else {
429                        "a tag"
430                    },
431                    self.attr_or_tag,
432                ));
433            }
434            Some(Box::new(help_text))
435        }
436    }
437
438    /// Tried to get an attribute of a (non-entity) record, but that record didn't
439    /// have that attribute
440    //
441    // CAUTION: this type is publicly exported in `cedar-policy`.
442    // Don't make fields `pub`, don't make breaking changes, and use caution
443    // when adding public methods.
444    #[derive(Debug, PartialEq, Eq, Clone, Error)]
445    #[error("record does not have the attribute `{attr}`")]
446    pub struct RecordAttrDoesNotExistError {
447        /// Name of the attribute we tried to access
448        pub(crate) attr: SmolStr,
449        /// (First five) Available attributes on the record
450        pub(crate) available_attrs: Vec<SmolStr>,
451        /// The total number of attrs this record has
452        pub(crate) total_attrs: usize,
453        /// Source location
454        pub(crate) source_loc: Option<Loc>,
455    }
456
457    impl Diagnostic for RecordAttrDoesNotExistError {
458        impl_diagnostic_from_source_loc_opt_field!(source_loc);
459
460        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
461            if self.available_attrs.is_empty() {
462                Some(Box::new("record does not have any attributes"))
463            } else if self.available_attrs.len() == self.total_attrs {
464                Some(Box::new(format!(
465                    "available attributes: {:?}",
466                    self.available_attrs
467                )))
468            } else {
469                Some(Box::new(format!(
470                    "available attributes: [{}, ... ({} more attributes) ]",
471                    self.available_attrs.iter().join(","),
472                    self.total_attrs - self.available_attrs.len()
473                )))
474            }
475        }
476    }
477
478    /// Tried to evaluate an operation on values with incorrect types for that
479    /// operation
480    //
481    // CAUTION: this type is publicly exported in `cedar-policy`.
482    // Don't make fields `pub`, don't make breaking changes, and use caution
483    // when adding public methods.
484    #[derive(Debug, PartialEq, Eq, Clone, Error)]
485    pub struct TypeError {
486        /// Expected one of these types
487        pub(crate) expected: NonEmpty<Type>,
488        /// Encountered this type instead
489        pub(crate) actual: Type,
490        /// Optional advice for how to fix this error
491        pub(crate) advice: Option<String>,
492        /// Source location
493        pub(crate) source_loc: Option<Loc>,
494    }
495
496    impl Diagnostic for TypeError {
497        impl_diagnostic_from_source_loc_opt_field!(source_loc);
498
499        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
500            self.advice.as_ref().map(|advice| Box::new(advice) as _)
501        }
502    }
503
504    impl std::fmt::Display for TypeError {
505        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
506            if self.expected.len() == 1 {
507                write!(
508                    f,
509                    "type error: expected {}, got {}",
510                    self.expected.first(),
511                    self.actual
512                )
513            } else {
514                write!(
515                    f,
516                    "type error: expected one of [{}], got {}",
517                    self.expected.iter().join(", "),
518                    self.actual
519                )
520            }
521        }
522    }
523
524    /// Wrong number of arguments provided to an extension function
525    //
526    // CAUTION: this type is publicly exported in `cedar-policy`.
527    // Don't make fields `pub`, don't make breaking changes, and use caution
528    // when adding public methods.
529    #[derive(Debug, PartialEq, Eq, Clone, Error)]
530    #[error("wrong number of arguments provided to extension function `{function_name}`: expected {expected}, got {actual}")]
531    pub struct WrongNumArgumentsError {
532        /// arguments to this function
533        pub(crate) function_name: Name,
534        /// expected number of arguments
535        pub(crate) expected: usize,
536        /// actual number of arguments
537        pub(crate) actual: usize,
538        /// Source location
539        pub(crate) source_loc: Option<Loc>,
540    }
541
542    impl Diagnostic for WrongNumArgumentsError {
543        impl_diagnostic_from_source_loc_opt_field!(source_loc);
544    }
545
546    /// Overflow during an integer operation
547    //
548    // CAUTION: this type is publicly exported in `cedar-policy`.
549    // Don't make fields `pub`, don't make breaking changes, and use caution
550    // when adding public methods.
551    #[derive(Debug, PartialEq, Eq, Clone, Diagnostic, Error)]
552    pub enum IntegerOverflowError {
553        /// Overflow during a binary operation
554        #[error(transparent)]
555        #[diagnostic(transparent)]
556        BinaryOp(#[from] BinaryOpOverflowError),
557
558        /// Overflow during a unary operation
559        #[error(transparent)]
560        #[diagnostic(transparent)]
561        UnaryOp(#[from] UnaryOpOverflowError),
562    }
563
564    impl IntegerOverflowError {
565        pub(crate) fn source_loc(&self) -> Option<&Loc> {
566            match self {
567                Self::BinaryOp(e) => e.source_loc.as_ref(),
568                Self::UnaryOp(e) => e.source_loc.as_ref(),
569            }
570        }
571
572        pub(crate) fn with_maybe_source_loc(self, source_loc: Option<Loc>) -> Self {
573            match self {
574                Self::BinaryOp(e) => Self::BinaryOp(BinaryOpOverflowError { source_loc, ..e }),
575                Self::UnaryOp(e) => Self::UnaryOp(UnaryOpOverflowError { source_loc, ..e }),
576            }
577        }
578    }
579
580    /// Overflow during a binary operation
581    //
582    // CAUTION: this type is publicly exported in `cedar-policy`.
583    // Don't make fields `pub`, don't make breaking changes, and use caution
584    // when adding public methods.
585    #[derive(Debug, PartialEq, Eq, Clone, Error)]
586    #[error("integer overflow while attempting to {} the values `{arg1}` and `{arg2}`", match .op { BinaryOp::Add => "add", BinaryOp::Sub => "subtract", BinaryOp::Mul => "multiply", _ => "perform an operation on" })]
587    pub struct BinaryOpOverflowError {
588        /// overflow while evaluating this operator
589        pub(crate) op: BinaryOp,
590        /// first argument to that operator
591        pub(crate) arg1: Value,
592        /// second argument to that operator
593        pub(crate) arg2: Value,
594        /// Source location
595        pub(crate) source_loc: Option<Loc>,
596    }
597
598    impl Diagnostic for BinaryOpOverflowError {
599        impl_diagnostic_from_source_loc_opt_field!(source_loc);
600    }
601
602    /// Overflow during a unary operation
603    //
604    // CAUTION: this type is publicly exported in `cedar-policy`.
605    // Don't make fields `pub`, don't make breaking changes, and use caution
606    // when adding public methods.
607    #[derive(Debug, PartialEq, Eq, Clone, Error)]
608    #[error("integer overflow while attempting to {} the value `{arg}`", match .op { UnaryOp::Neg => "negate", _ => "perform an operation on" })]
609    pub struct UnaryOpOverflowError {
610        /// overflow while evaluating this operator
611        pub(crate) op: UnaryOp,
612        /// argument to that operator
613        pub(crate) arg: Value,
614        /// Source location
615        pub(crate) source_loc: Option<Loc>,
616    }
617
618    impl Diagnostic for UnaryOpOverflowError {
619        impl_diagnostic_from_source_loc_opt_field!(source_loc);
620    }
621
622    /// Not all template slots were linked
623    //
624    // CAUTION: this type is publicly exported in `cedar-policy`.
625    // Don't make fields `pub`, don't make breaking changes, and use caution
626    // when adding public methods.
627    #[derive(Debug, PartialEq, Eq, Clone, Error)]
628    #[error("template slot `{slot}` was not linked")]
629    pub struct UnlinkedSlotError {
630        /// Slot which was not linked
631        pub(crate) slot: SlotId,
632        /// Source location
633        pub(crate) source_loc: Option<Loc>,
634    }
635
636    impl Diagnostic for UnlinkedSlotError {
637        impl_diagnostic_from_source_loc_opt_field!(source_loc);
638    }
639
640    /// Evaluation error thrown by an extension function
641    //
642    // CAUTION: this type is publicly exported in `cedar-policy`.
643    // Don't make fields `pub`, don't make breaking changes, and use caution
644    // when adding public methods.
645    #[derive(Debug, PartialEq, Eq, Clone, Error)]
646    #[error("error while evaluating `{extension_name}` extension function: {msg}")]
647    pub struct ExtensionFunctionExecutionError {
648        /// Name of the extension throwing the error
649        pub(crate) extension_name: Name,
650        /// Error message from the extension
651        pub(crate) msg: String,
652        /// Source location
653        pub(crate) source_loc: Option<Loc>,
654    }
655
656    impl Diagnostic for ExtensionFunctionExecutionError {
657        impl_diagnostic_from_source_loc_opt_field!(source_loc);
658    }
659
660    impl ExtensionFunctionExecutionError {
661        /// Get the name of the extension that threw this error
662        pub fn extension_name(&self) -> String {
663            self.extension_name.to_string()
664        }
665    }
666
667    /// This error is raised if an expression contains unknowns and cannot be
668    /// reduced to a [`Value`]. In order to return partial results, use the
669    /// partial evaluation APIs instead.
670    //
671    // CAUTION: this type is publicly exported in `cedar-policy`.
672    // Don't make fields `pub`, don't make breaking changes, and use caution
673    // when adding public methods.
674    #[derive(Debug, PartialEq, Eq, Clone, Error)]
675    #[error("the expression contains unknown(s): `{expr}`")]
676    pub struct NonValueError {
677        /// Expression that contained unknown(s)
678        pub(crate) expr: Expr,
679        /// Source location
680        pub(crate) source_loc: Option<Loc>,
681    }
682
683    impl Diagnostic for NonValueError {
684        impl_diagnostic_from_source_loc_opt_field!(source_loc);
685
686        fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
687            Some(Box::new("consider using the partial evaluation APIs"))
688        }
689    }
690
691    /// Maximum recursion limit reached for expression evaluation
692    //
693    // CAUTION: this type is publicly exported in `cedar-policy`.
694    // Don't make fields `pub`, don't make breaking changes, and use caution
695    // when adding public methods.
696    #[derive(Debug, PartialEq, Eq, Clone, Error)]
697    #[error("recursion limit reached")]
698    pub struct RecursionLimitError {
699        /// Source location
700        pub(crate) source_loc: Option<Loc>,
701    }
702
703    impl Diagnostic for RecursionLimitError {
704        impl_diagnostic_from_source_loc_opt_field!(source_loc);
705    }
706}
707
708/// Type alias for convenience
709pub type Result<T> = std::result::Result<T, EvaluationError>;