cedar_policy_core/entities/json/
err.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use super::SchemaType;
18use crate::ast::{EntityUID, Expr, ExprKind, Name, RestrictedExpr, RestrictedExpressionError};
19use crate::extensions::ExtensionsError;
20use smol_str::SmolStr;
21use thiserror::Error;
22
23/// Errors thrown during deserialization from JSON
24#[derive(Debug, Error)]
25pub enum JsonDeserializationError {
26    /// Error thrown by `serde_json`
27    #[error("{0}")]
28    Serde(#[from] serde_json::Error),
29    /// Contents of an `__expr` escape failed to parse as a Cedar expression.
30    ///
31    /// `__expr` is deprecated (starting with the 1.2 release), and once it is
32    /// removed, this error will also be removed.
33    #[error(transparent)]
34    ExprParseError(crate::parser::err::ParseError),
35    /// Contents of an `__entity` escape failed to parse as an entity reference
36    #[error(transparent)]
37    EntityParseError(crate::parser::err::ParseError),
38    /// Function name in an `__extn` escape failed to parse as an extension function name
39    #[error(transparent)]
40    ExtnParseError(crate::parser::err::ParseError),
41    /// Restricted expression error
42    #[error(transparent)]
43    RestrictedExpressionError(#[from] RestrictedExpressionError),
44    /// Error thrown by an operation on `Extensions`
45    #[error(transparent)]
46    ExtensionsError(#[from] ExtensionsError),
47    /// A field that needs to be a literal entity reference, was some other JSON value
48    #[error("{ctx}, expected a literal entity reference, but got {got}")]
49    ExpectedLiteralEntityRef {
50        /// Context of this error
51        ctx: JsonDeserializationErrorContext,
52        /// the expression we got instead
53        got: Box<Expr>,
54    },
55    /// A field that needs to be an extension value, was some other JSON value
56    #[error("{ctx}, expected an extension value, but got {got}")]
57    ExpectedExtnValue {
58        /// Context of this error
59        ctx: JsonDeserializationErrorContext,
60        /// the expression we got instead
61        got: Box<Expr>,
62    },
63    /// Contexts need to be records, but we got some other JSON value
64    #[error("Expected Context to be a record, but got {got}")]
65    ExpectedContextToBeRecord {
66        /// Expression we got instead
67        got: Box<RestrictedExpr>,
68    },
69    /// Schema-based parsing needed an implicit extension constructor, but no suitable
70    /// constructor was found
71    #[error("Extension constructor for {arg_type} -> {return_type} not found")]
72    ImpliedConstructorNotFound {
73        /// return type of the constructor we were looking for
74        return_type: Box<SchemaType>,
75        /// argument type of the constructor we were looking for
76        arg_type: Box<SchemaType>,
77    },
78    /// During schema-based parsing, encountered this attribute on this entity, but that
79    /// attribute shouldn't exist on entities of this type
80    #[error("Attribute {:?} on {uid} shouldn't exist according to the schema", &.attr)]
81    UnexpectedEntityAttr {
82        /// Entity that had the unexpected attribute
83        uid: EntityUID,
84        /// Name of the attribute that was unexpected
85        attr: SmolStr,
86    },
87    /// During schema-based parsing, encountered this attribute on a record, but
88    /// that attribute shouldn't exist on that record
89    #[error("{ctx}, record attribute {record_attr:?} shouldn't exist according to the schema")]
90    UnexpectedRecordAttr {
91        /// Context of this error
92        ctx: JsonDeserializationErrorContext,
93        /// Name of the (Record) attribute which was unexpected
94        record_attr: SmolStr,
95    },
96    /// During schema-based parsing, didn't encounter this attribute of a
97    /// record, but that attribute should have existed
98    #[error("Expected {uid} to have an attribute {attr:?}, but it didn't")]
99    MissingRequiredEntityAttr {
100        /// Entity that is missing a required attribute
101        uid: EntityUID,
102        /// Name of the attribute which was expected
103        attr: SmolStr,
104    },
105    /// During schema-based parsing, didn't encounter this attribute of a
106    /// record, but that attribute should have existed
107    #[error("{ctx}, expected the record to have an attribute {record_attr:?}, but it didn't")]
108    MissingRequiredRecordAttr {
109        /// Context of this error
110        ctx: JsonDeserializationErrorContext,
111        /// Name of the (Record) attribute which was expected
112        record_attr: SmolStr,
113    },
114    /// During schema-based parsing, the given attribute on the given entity had
115    /// a different type than the schema indicated to expect
116    #[error("{ctx}, type mismatch: attribute was expected to have type {expected}, but actually has type {actual}")]
117    TypeMismatch {
118        /// Context of this error
119        ctx: JsonDeserializationErrorContext,
120        /// Type which was expected
121        expected: Box<SchemaType>,
122        /// Type which was encountered instead
123        actual: Box<SchemaType>,
124    },
125    /// During schema-based parsing, found a set whose elements don't all have the
126    /// same type.  This doesn't match any possible schema.
127    #[error("{ctx}, set elements have different types: {ty1} and {ty2}")]
128    HeterogeneousSet {
129        /// Context of this error
130        ctx: JsonDeserializationErrorContext,
131        /// First element type which was found
132        ty1: Box<SchemaType>,
133        /// Second element type which was found
134        ty2: Box<SchemaType>,
135    },
136}
137
138/// Errors thrown during serialization to JSON
139#[derive(Debug, Error)]
140pub enum JsonSerializationError {
141    /// Error thrown by `serde_json`
142    #[error("{0}")]
143    Serde(#[from] serde_json::Error),
144    /// Extension-function calls with 0 arguments are not currently supported in
145    /// our JSON format.
146    #[error("extension-function calls with 0 arguments are not currently supported in our JSON format. found call of {func}")]
147    ExtnCall0Arguments {
148        /// Name of the function which was called with 0 arguments
149        func: Name,
150    },
151    /// Extension-function calls with 2 or more arguments are not currently
152    /// supported in our JSON format.
153    #[error("extension-function calls with 2 or more arguments are not currently supported in our JSON format. found call of {func}")]
154    ExtnCall2OrMoreArguments {
155        /// Name of the function which was called with 2 or more arguments
156        func: Name,
157    },
158    /// Encountered a `Record` which can't be serialized to JSON because it
159    /// contains a key which is reserved as a JSON escape.
160    #[error("record uses reserved key: {key}")]
161    ReservedKey {
162        /// Reserved key which was used by the `Record`
163        key: SmolStr,
164    },
165    /// Encountered an `ExprKind` which we didn't expect. Either a case is
166    /// missing in `JSONValue::from_expr()`, or an internal invariant was
167    /// violated and there is a non-restricted expression in `RestrictedExpr`
168    #[error("unexpected restricted expression: {kind:?}")]
169    UnexpectedRestrictedExprKind {
170        /// `ExprKind` which we didn't expect to find
171        kind: ExprKind,
172    },
173}
174
175/// Gives information about the context of a JSON deserialization error (e.g.,
176/// where we were in the JSON document).
177#[derive(Debug, Clone)]
178pub enum JsonDeserializationErrorContext {
179    /// The error occurred while deserializing the attribute `attr` of an entity.
180    EntityAttribute {
181        /// Entity where the error occurred
182        uid: EntityUID,
183        /// Attribute where the error occurred
184        attr: SmolStr,
185    },
186    /// The error occurred while deserializing the `parents` field of an entity.
187    EntityParents {
188        /// Entity where the error occurred
189        uid: EntityUID,
190    },
191    /// The error occurred while deserializing the `uid` field of an entity.
192    EntityUid,
193    /// The error occurred while deserializing the `Context`.
194    Context,
195}
196
197impl std::fmt::Display for JsonDeserializationErrorContext {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        match self {
200            Self::EntityAttribute { uid, attr } => write!(f, "In attribute {attr:?} on {uid}"),
201            Self::EntityParents { uid } => write!(f, "In parents field of {uid}"),
202            Self::EntityUid => write!(f, "In uid field of <unknown entity>"),
203            Self::Context => write!(f, "While parsing Context"),
204        }
205    }
206}