1use std::collections::HashSet;
25
26use crate::extensions::ExtensionFunctionLookupError;
27use crate::pst;
28use miette::Diagnostic;
29use smol_str::ToSmolStr;
30use thiserror::Error;
31
32use crate::ast;
33use crate::est;
34
35#[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
37#[non_exhaustive]
38pub enum PstConstructionError {
39 #[error(transparent)]
41 #[diagnostic(transparent)]
42 PolicyMissingLinkId(#[from] error_body::PolicyMissingLinkIdError),
43
44 #[error(transparent)]
46 #[diagnostic(transparent)]
47 ActionConstraintCannotHaveSlots(#[from] error_body::ActionConstraintCannotHaveSlotsError),
48
49 #[error(transparent)]
51 #[diagnostic(transparent)]
52 ExpectedTemplateWithSlots(#[from] error_body::ExpectedTemplateWithSlotsError),
53
54 #[error(transparent)]
56 #[diagnostic(transparent)]
57 WrongSlotPosition(#[from] error_body::WrongSlotPositionError),
58
59 #[error(transparent)]
61 #[diagnostic(transparent)]
62 DuplicateRecordKey(#[from] error_body::DuplicateRecordKeyError),
63
64 #[error(transparent)]
66 #[diagnostic(transparent)]
67 InvalidAnnotation(#[from] error_body::InvalidAnnotationError),
68
69 #[error(transparent)]
71 #[diagnostic(transparent)]
72 InvalidEntityUid(#[from] error_body::InvalidEntityUidError),
73
74 #[error(transparent)]
76 #[diagnostic(transparent)]
77 InvalidEntityType(#[from] error_body::InvalidEntityTypeError),
78
79 #[error(transparent)]
81 #[diagnostic(transparent)]
82 InvalidExpression(#[from] error_body::InvalidExpressionError),
83
84 #[error(transparent)]
86 #[diagnostic(transparent)]
87 UnknownFunction(#[from] error_body::UnknownFunctionError),
88
89 #[error(transparent)]
91 #[diagnostic(transparent)]
92 WrongArity(#[from] error_body::WrongArityError),
93
94 #[error(transparent)]
96 #[diagnostic(transparent)]
97 UnsupportedErrorNode(#[from] error_body::UnsupportedErrorNode),
98
99 #[error(transparent)]
101 #[diagnostic(transparent)]
102 ParsingFailed(#[from] error_body::ParsingFailedError),
103
104 #[error(transparent)]
106 #[diagnostic(transparent)]
107 LinkingFailed(#[from] error_body::LinkingError),
108
109 #[error(transparent)]
111 #[diagnostic(transparent)]
112 ContainsSlots(#[from] error_body::ContainsSlotError),
113}
114
115#[doc(hidden)]
116impl From<est::FromJsonError> for PstConstructionError {
117 fn from(err: est::FromJsonError) -> Self {
118 match err {
119 est::FromJsonError::UnknownExtensionFunction(e) => {
120 error_body::UnknownFunctionError::new(e.to_smolstr()).into()
121 }
122 est::FromJsonError::InvalidEntityType(e) => error_body::InvalidEntityTypeError {
123 description: e.to_string(),
124 }
125 .into(),
126 est::FromJsonError::JsonDeserializationError(e) => {
127 error_body::ParsingFailedError::new(e.to_string()).into()
130 }
131 est::FromJsonError::UnescapeError(e) => {
132 error_body::ParsingFailedError::new(e.head.to_string()).into()
134 }
135
136 est::FromJsonError::ActionSlot => {
139 error_body::ActionConstraintCannotHaveSlotsError.into()
140 }
141 est::FromJsonError::InvalidActionType(e) => error_body::InvalidEntityTypeError {
142 description: e.to_string(),
143 }
144 .into(),
145 est::FromJsonError::InvalidSlotName => {
146 error_body::ParsingFailedError::new(err.to_string()).into()
147 }
148 est::FromJsonError::TemplateToPolicy(e) => {
149 let mut slots: HashSet<pst::SlotId, _> = HashSet::new();
150 slots.insert(e.slot.id.into());
151 error_body::ContainsSlotError { slots }.into()
152 }
153 est::FromJsonError::PolicyToTemplate(_) => {
154 error_body::ExpectedTemplateWithSlotsError.into()
155 }
156 est::FromJsonError::SlotsInConditionClause(e) => error_body::ContainsSlotError {
157 slots: std::iter::once(e.slot.id.into()).collect(),
158 }
159 .into(),
160 est::FromJsonError::MissingOperator | est::FromJsonError::MultipleOperators { .. } => {
161 error_body::InvalidExpressionError::new(err.to_string()).into()
162 }
163 #[cfg(feature = "tolerant-ast")]
164 est::FromJsonError::ASTErrorNode => {
165 error_body::UnsupportedErrorNode::new("AST contains an error node").into()
166 }
167 }
168 }
169}
170
171#[doc(hidden)]
172impl From<ast::ExpressionConstructionError> for PstConstructionError {
173 fn from(err: ast::ExpressionConstructionError) -> Self {
174 let ast::ExpressionConstructionError::DuplicateKey(k) = err;
175 error_body::DuplicateRecordKeyError { key: k.key }.into()
176 }
177}
178
179#[doc(hidden)]
180impl From<crate::parser::err::ParseErrors> for PstConstructionError {
181 fn from(value: crate::parser::err::ParseErrors) -> Self {
182 error_body::ParsingFailedError::from(value).into()
183 }
184}
185
186#[doc(hidden)]
189impl From<ExtensionFunctionLookupError> for PstConstructionError {
190 fn from(err: ExtensionFunctionLookupError) -> Self {
191 let ExtensionFunctionLookupError::FuncDoesNotExist(body) = err;
192 error_body::UnknownFunctionError::new(body.name.to_smolstr()).into()
193 }
194}
195
196pub mod error_body {
198 use miette::Diagnostic;
199 use smol_str::SmolStr;
200 use std::collections::HashSet;
201 use thiserror::Error;
202
203 use crate::est;
204 use crate::pst;
205
206 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
208 #[error("linked policy is missing an instance id")]
209 pub struct PolicyMissingLinkIdError;
210
211 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
213 #[error("action constraint cannot have slots")]
214 pub struct ActionConstraintCannotHaveSlotsError;
215
216 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
218 #[error("expected a template with slots, but found a static policy")]
219 pub struct ExpectedTemplateWithSlotsError;
220
221 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
223 #[error("slot `{found}` cannot be used in this position (expected slot `{expected}`)")]
224 pub struct WrongSlotPositionError {
225 found: pst::SlotId,
226 expected: pst::SlotId,
227 }
228
229 impl WrongSlotPositionError {
230 pub(crate) fn new(found: pst::SlotId, expected: pst::SlotId) -> Self {
231 Self { found, expected }
232 }
233 }
234
235 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
237 #[error("duplicate record key: `{key}`")]
238 pub struct DuplicateRecordKeyError {
239 pub(crate) key: SmolStr,
240 }
241
242 impl DuplicateRecordKeyError {
243 pub fn key(&self) -> &str {
245 &self.key
246 }
247 }
248
249 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
251 #[error("invalid annotation: {description}")]
252 pub struct InvalidAnnotationError {
253 description: String,
254 }
255
256 impl InvalidAnnotationError {
257 pub(crate) fn new(description: impl Into<String>) -> Self {
258 Self {
259 description: description.into(),
260 }
261 }
262 }
263
264 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
266 #[error("invalid entity UID: {description}")]
267 pub struct InvalidEntityUidError {
268 pub(crate) description: String,
269 }
270
271 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
273 #[error("invalid entity type: `{description}`")]
274 pub struct InvalidEntityTypeError {
275 pub(crate) description: String,
276 }
277
278 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
280 #[error("invalid expression: {description}")]
281 pub struct InvalidExpressionError {
282 pub(crate) description: String,
283 }
284
285 impl InvalidExpressionError {
286 pub(crate) fn new(description: String) -> Self {
287 Self { description }
288 }
289 }
290
291 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
293 #[error("unknown function: `{name}`")]
294 pub struct UnknownFunctionError {
295 pub(crate) name: SmolStr,
296 }
297
298 impl UnknownFunctionError {
299 pub(crate) fn new(name: SmolStr) -> Self {
300 Self { name }
301 }
302
303 pub fn name(&self) -> &str {
305 &self.name
306 }
307 }
308
309 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
311 #[error("function `{name}` expects {expected} argument(s), got {got}")]
312 pub struct WrongArityError {
313 pub(crate) name: String,
314 pub(crate) expected: usize,
315 pub(crate) got: usize,
316 }
317
318 impl WrongArityError {
319 pub(crate) fn new(name: String, expected: usize, got: usize) -> Self {
320 Self {
321 name,
322 expected,
323 got,
324 }
325 }
326
327 pub fn name(&self) -> &str {
329 &self.name
330 }
331
332 pub fn expected(&self) -> usize {
334 self.expected
335 }
336
337 pub fn got(&self) -> usize {
339 self.got
340 }
341 }
342
343 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
345 #[error("error nodes not supported in conversion: {description}")]
346 pub struct UnsupportedErrorNode {
347 description: &'static str,
349 }
350
351 impl UnsupportedErrorNode {
352 pub(crate) fn new(description: &'static str) -> Self {
353 Self { description }
354 }
355 }
356
357 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
359 #[error("parse error: {description}")]
360 pub struct ParsingFailedError {
361 pub(crate) description: String,
362 }
363
364 impl ParsingFailedError {
365 pub(crate) fn new(description: String) -> Self {
366 Self { description }
367 }
368 }
369
370 impl From<crate::parser::err::ParseErrors> for ParsingFailedError {
371 fn from(value: crate::parser::err::ParseErrors) -> Self {
372 Self::new(format!("{value}"))
373 }
374 }
375
376 #[derive(Debug, PartialEq, Eq, Diagnostic, Error, Clone)]
378 pub enum LinkingError {
379 #[error("failed to link template: no value provided for `{slot}`")]
381 MissedSlot {
382 slot: pst::SlotId,
384 },
385 }
386
387 impl From<LinkingError> for est::LinkingError {
388 fn from(err: LinkingError) -> Self {
389 match err {
390 LinkingError::MissedSlot { slot } => Self::MissedSlot { slot: slot.into() },
391 }
392 }
393 }
394
395 #[derive(Debug, Clone, PartialEq, Eq, Diagnostic, Error)]
397 #[error("policy or expression contains slots: {slots:?}")]
398 pub struct ContainsSlotError {
399 pub(crate) slots: HashSet<crate::pst::SlotId>,
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use super::*;
406 use cool_asserts::assert_matches;
407
408 #[test]
409 fn from_json_error_conversions() {
410 use crate::est::FromJsonError;
411
412 let serde_err = serde_json::from_str::<String>("bad").unwrap_err();
420 let json_deser_err: crate::entities::json::err::JsonDeserializationError = serde_err.into();
421 let err: PstConstructionError =
422 FromJsonError::JsonDeserializationError(json_deser_err).into();
423 assert_matches!(err, PstConstructionError::ParsingFailed(..));
424
425 let err: PstConstructionError = FromJsonError::ActionSlot.into();
427 assert!(matches!(
428 err,
429 PstConstructionError::ActionConstraintCannotHaveSlots(..)
430 ));
431
432 let euid = ast::EntityUID::with_eid_and_type("Bad", "act").unwrap();
434 let err: PstConstructionError =
435 FromJsonError::InvalidActionType(crate::parser::err::parse_errors::InvalidActionType {
436 euids: nonempty::nonempty![std::sync::Arc::new(euid)],
437 })
438 .into();
439 assert_matches!(err, PstConstructionError::InvalidEntityType(..));
440
441 let err: PstConstructionError = FromJsonError::InvalidSlotName.into();
443 assert_matches!(err, PstConstructionError::ParsingFailed(..));
444
445 let err: PstConstructionError = FromJsonError::TemplateToPolicy(
447 crate::parser::err::parse_errors::ExpectedStaticPolicy {
448 slot: ast::Slot {
449 id: ast::SlotId::principal(),
450 loc: None,
451 },
452 },
453 )
454 .into();
455 assert_matches!(err, PstConstructionError::ContainsSlots(..));
456
457 let err: PstConstructionError = FromJsonError::PolicyToTemplate(
459 crate::parser::err::parse_errors::ExpectedTemplate::new(),
460 )
461 .into();
462 assert!(matches!(
463 err,
464 PstConstructionError::ExpectedTemplateWithSlots(..)
465 ));
466
467 let err: PstConstructionError = FromJsonError::SlotsInConditionClause(
469 crate::parser::err::parse_errors::SlotsInConditionClause {
470 slot: ast::Slot {
471 id: ast::SlotId::resource(),
472 loc: None,
473 },
474 clause_type: "when",
475 },
476 )
477 .into();
478 assert_matches!(err, PstConstructionError::ContainsSlots(..));
479
480 let err: PstConstructionError = FromJsonError::MissingOperator.into();
482 assert_matches!(err, PstConstructionError::InvalidExpression(..));
483
484 let err: PstConstructionError = FromJsonError::MultipleOperators {
486 ops: vec!["a".into(), "b".into()],
487 }
488 .into();
489 assert_matches!(err, PstConstructionError::InvalidExpression(..));
490 }
491
492 #[test]
493 fn from_expression_construction_error() {
494 let err: PstConstructionError = ast::ExpressionConstructionError::DuplicateKey(
495 ast::expression_construction_errors::DuplicateKeyError {
496 key: "k".into(),
497 context: "in record literal",
498 },
499 )
500 .into();
501 assert_matches!(err, PstConstructionError::DuplicateRecordKey(..));
502 }
503
504 #[test]
505 fn from_extension_function_lookup_error() {
506 use crate::extensions::ExtensionFunctionLookupError;
507 let err: PstConstructionError = ExtensionFunctionLookupError::FuncDoesNotExist(
508 crate::extensions::extension_function_lookup_errors::FuncDoesNotExistError {
509 name: "bogus".parse().unwrap(),
510 source_loc: None,
511 },
512 )
513 .into();
514 assert_matches!(err, PstConstructionError::UnknownFunction(..));
515 }
516}