1use crate::{
18 ast::{EntityUID, ReservedNameError},
19 transitive_closure,
20};
21use itertools::{Either, Itertools};
22use miette::Diagnostic;
23use nonempty::NonEmpty;
24use thiserror::Error;
25
26use crate::validator::cedar_schema;
27
28#[derive(Debug, Error, Diagnostic)]
30pub enum CedarSchemaError {
31 #[error(transparent)]
33 #[diagnostic(transparent)]
34 Schema(#[from] SchemaError),
35 #[error(transparent)]
37 IO(#[from] std::io::Error),
38 #[error(transparent)]
40 #[diagnostic(transparent)]
41 Parsing(#[from] CedarSchemaParseError),
42}
43
44#[derive(Debug, Error)]
47#[error("error parsing schema: {errs}")]
48pub struct CedarSchemaParseError {
49 errs: cedar_schema::parser::CedarSchemaParseErrors,
51 suspect_json_format: bool,
54}
55
56impl Diagnostic for CedarSchemaParseError {
57 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
58 let suspect_json_help = if self.suspect_json_format {
59 Some(Box::new("this API was expecting a schema in the Cedar schema format; did you mean to use a different function, which expects a JSON-format Cedar schema"))
60 } else {
61 None
62 };
63 match (suspect_json_help, self.errs.help()) {
64 (Some(json), Some(inner)) => Some(Box::new(format!("{inner}\n{json}"))),
65 (Some(h), None) => Some(h),
66 (None, Some(h)) => Some(h),
67 (None, None) => None,
68 }
69 }
70
71 fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
74 self.errs.code()
75 }
76 fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
77 self.errs.labels()
78 }
79 fn severity(&self) -> Option<miette::Severity> {
80 self.errs.severity()
81 }
82 fn url<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
83 self.errs.url()
84 }
85 fn source_code(&self) -> Option<&dyn miette::SourceCode> {
86 self.errs.source_code()
87 }
88 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
89 self.errs.diagnostic_source()
90 }
91 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
92 self.errs.related()
93 }
94}
95
96impl CedarSchemaParseError {
97 pub(crate) fn new(errs: cedar_schema::parser::CedarSchemaParseErrors, src: &str) -> Self {
101 let suspect_json_format = match src.trim_start().chars().next() {
103 None => false, Some('{') => true, Some(_) => false, };
107 Self {
108 errs,
109 suspect_json_format,
110 }
111 }
112
113 pub fn suspect_json_format(&self) -> bool {
118 self.suspect_json_format
119 }
120
121 pub fn errors(&self) -> &cedar_schema::parser::CedarSchemaParseErrors {
123 &self.errs
124 }
125}
126
127#[derive(Debug, Diagnostic, Error)]
133#[non_exhaustive]
134pub enum SchemaError {
135 #[error(transparent)]
137 #[diagnostic(transparent)]
138 JsonSerialization(#[from] schema_errors::JsonSerializationError),
139 #[error(transparent)]
141 #[diagnostic(transparent)]
142 JsonDeserialization(#[from] schema_errors::JsonDeserializationError),
143 #[error(transparent)]
146 #[diagnostic(transparent)]
147 ActionTransitiveClosure(#[from] schema_errors::ActionTransitiveClosureError),
148 #[error(transparent)]
151 #[diagnostic(transparent)]
152 EntityTypeTransitiveClosure(#[from] schema_errors::EntityTypeTransitiveClosureError),
153 #[error(transparent)]
155 #[diagnostic(transparent)]
156 UnsupportedFeature(#[from] schema_errors::UnsupportedFeatureError),
157 #[error(transparent)]
162 #[diagnostic(transparent)]
163 UndeclaredEntityTypes(#[from] schema_errors::UndeclaredEntityTypesError),
164 #[error(transparent)]
169 #[diagnostic(transparent)]
170 UndeclaredActionDescendants(#[from] schema_errors::UndeclaredActionsDescendantError),
171 #[error(transparent)]
174 #[diagnostic(transparent)]
175 TypeNotDefined(#[from] schema_errors::TypeNotDefinedError),
176 #[error(transparent)]
180 #[diagnostic(transparent)]
181 ActionNotDefined(#[from] schema_errors::ActionNotDefinedError),
182 #[error(transparent)]
186 #[diagnostic(transparent)]
187 TypeShadowing(#[from] schema_errors::TypeShadowingError),
188 #[error(transparent)]
192 #[diagnostic(transparent)]
193 ActionShadowing(#[from] schema_errors::ActionShadowingError),
194 #[error(transparent)]
196 #[diagnostic(transparent)]
197 DuplicateEntityType(#[from] schema_errors::DuplicateEntityTypeError),
198 #[error(transparent)]
200 #[diagnostic(transparent)]
201 DuplicateAction(#[from] schema_errors::DuplicateActionError),
202 #[error(transparent)]
204 #[diagnostic(transparent)]
205 DuplicateCommonType(#[from] schema_errors::DuplicateCommonTypeError),
206 #[error(transparent)]
208 #[diagnostic(transparent)]
209 CycleInActionHierarchy(#[from] schema_errors::CycleInActionHierarchyError),
210 #[error(transparent)]
212 #[diagnostic(transparent)]
213 CycleInCommonTypeReferences(#[from] schema_errors::CycleInCommonTypeReferencesError),
214 #[error(transparent)]
219 #[diagnostic(transparent)]
220 ActionEntityTypeDeclared(#[from] schema_errors::ActionEntityTypeDeclaredError),
221 #[error(transparent)]
223 #[diagnostic(transparent)]
224 ContextOrShapeNotRecord(#[from] schema_errors::ContextOrShapeNotRecordError),
225 #[error(transparent)]
227 #[diagnostic(transparent)]
228 #[deprecated = "this error is deprecated and should never be returned"]
229 #[expect(deprecated, reason = "inner variant is deprecated too")]
230 ActionAttributesContainEmptySet(#[from] schema_errors::ActionAttributesContainEmptySetError),
231 #[error(transparent)]
233 #[diagnostic(transparent)]
234 #[deprecated = "this error is deprecated and should never be returned"]
235 #[expect(deprecated, reason = "inner variant is deprecated too")]
236 UnsupportedActionAttribute(#[from] schema_errors::UnsupportedActionAttributeError),
237 #[error(transparent)]
239 #[diagnostic(transparent)]
240 #[deprecated = "this error is deprecated and should never be returned"]
241 #[expect(deprecated, reason = "inner variant is deprecated too")]
242 ActionAttrEval(#[from] schema_errors::ActionAttrEvalError),
243 #[error(transparent)]
245 #[diagnostic(transparent)]
246 #[deprecated = "this error is deprecated and should never be returned"]
247 #[expect(deprecated, reason = "inner variant is deprecated too")]
248 ExprEscapeUsed(#[from] schema_errors::ExprEscapeUsedError),
249 #[error(transparent)]
251 #[diagnostic(transparent)]
252 UnknownExtensionType(schema_errors::UnknownExtensionTypeError),
253 #[error(transparent)]
255 #[diagnostic(transparent)]
256 ReservedName(#[from] ReservedNameError),
257 #[error(transparent)]
260 #[diagnostic(transparent)]
261 CommonTypeInvariantViolation(#[from] schema_errors::CommonTypeInvariantViolationError),
262 #[error(transparent)]
265 #[diagnostic(transparent)]
266 ActionInvariantViolation(#[from] schema_errors::ActionInvariantViolationError),
267 #[error(transparent)]
269 #[diagnostic(transparent)]
270 InvalidActionType(#[from] schema_errors::InvalidActionTypeError),
271 #[error(transparent)]
274 #[diagnostic(transparent)]
275 EnumEntityInHierarchy(#[from] schema_errors::EnumEntityInHierarchyError),
276}
277
278impl From<transitive_closure::TcError<EntityUID>> for SchemaError {
279 fn from(e: transitive_closure::TcError<EntityUID>) -> Self {
280 match e {
284 transitive_closure::TcError::MissingTcEdge { .. } => {
285 SchemaError::ActionTransitiveClosure(Box::new(e).into())
286 }
287 transitive_closure::TcError::HasCycle(err) => {
288 schema_errors::CycleInActionHierarchyError {
289 uid: err.vertex_with_loop().clone(),
290 }
291 .into()
292 }
293 }
294 }
295}
296
297impl SchemaError {
298 pub fn join_nonempty(errs: NonEmpty<SchemaError>) -> SchemaError {
301 let (type_ndef_errors, non_type_ndef_errors): (Vec<_>, Vec<_>) =
305 errs.into_iter().partition_map(|e| match e {
306 SchemaError::TypeNotDefined(e) => Either::Left(e),
307 _ => Either::Right(e),
308 });
309 if let Some(errs) = NonEmpty::from_vec(type_ndef_errors) {
310 schema_errors::TypeNotDefinedError::join_nonempty(errs).into()
311 } else {
312 let (action_ndef_errors, other_errors): (Vec<_>, Vec<_>) =
313 non_type_ndef_errors.into_iter().partition_map(|e| match e {
314 SchemaError::ActionNotDefined(e) => Either::Left(e),
315 _ => Either::Right(e),
316 });
317 if let Some(errs) = NonEmpty::from_vec(action_ndef_errors) {
318 schema_errors::ActionNotDefinedError::join_nonempty(errs).into()
319 } else {
320 #[expect(
326 clippy::expect_used,
327 reason = "other_errors cannot be empty due to partitioning logic explained in comment above"
328 )]
329 other_errors.into_iter().next().expect("cannot be empty")
330 }
331 }
332 }
333}
334
335impl From<NonEmpty<SchemaError>> for SchemaError {
336 fn from(errs: NonEmpty<SchemaError>) -> Self {
337 Self::join_nonempty(errs)
338 }
339}
340
341impl From<NonEmpty<schema_errors::ActionNotDefinedError>> for SchemaError {
342 fn from(errs: NonEmpty<schema_errors::ActionNotDefinedError>) -> Self {
343 Self::ActionNotDefined(schema_errors::ActionNotDefinedError::join_nonempty(errs))
344 }
345}
346
347impl From<NonEmpty<schema_errors::TypeNotDefinedError>> for SchemaError {
348 fn from(errs: NonEmpty<schema_errors::TypeNotDefinedError>) -> Self {
349 Self::TypeNotDefined(schema_errors::TypeNotDefinedError::join_nonempty(errs))
350 }
351}
352
353pub type Result<T> = std::result::Result<T, SchemaError>;
355
356pub mod schema_errors {
358
359 #![expect(deprecated, reason = "see comment immediately above")]
363
364 use std::fmt::Display;
365
366 use crate::ast::{EntityType, EntityUID, InternalName, Name};
367 use crate::fuzzy_match::fuzzy_search;
368 use crate::parser::{join_with_conjunction, Loc};
369 use crate::transitive_closure;
370 use crate::validator::schema::Extensions;
371 use itertools::Itertools;
372 use miette::Diagnostic;
373 use nonempty::NonEmpty;
374 use smol_str::SmolStr;
375 use thiserror::Error;
376
377 #[derive(Debug, Diagnostic, Error)]
383 #[error(transparent)]
384 pub struct JsonSerializationError(#[from] pub(crate) serde_json::Error);
385
386 #[derive(Debug, Diagnostic, Error)]
392 #[error("transitive closure computation/enforcement error on action hierarchy")]
393 #[diagnostic(transparent)]
394 pub struct ActionTransitiveClosureError(
395 #[from] pub(crate) Box<transitive_closure::TcError<EntityUID>>,
396 );
397
398 #[derive(Debug, Diagnostic, Error)]
404 #[error("transitive closure computation/enforcement error on entity type hierarchy")]
405 #[diagnostic(transparent)]
406 pub struct EntityTypeTransitiveClosureError(
407 #[from] pub(crate) Box<transitive_closure::TcError<EntityType>>,
408 );
409
410 #[derive(Debug, Error)]
416 pub struct UndeclaredEntityTypesError {
417 pub(crate) types: NonEmpty<EntityType>,
419 }
420
421 impl Display for UndeclaredEntityTypesError {
422 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
423 if self.types.len() == 1 {
424 write!(f, "undeclared entity type: ")?;
425 } else {
426 write!(f, "undeclared entity types: ")?;
427 }
428 join_with_conjunction(f, "and", self.types.iter().sorted_unstable(), |f, s| {
429 s.fmt(f)
430 })
431 }
432 }
433
434 impl Diagnostic for UndeclaredEntityTypesError {
435 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
436 Some(Box::new("any entity types appearing anywhere in a schema need to be declared in `entityTypes`"))
437 }
438
439 impl_diagnostic_from_method_on_nonempty_field!(types, loc);
440 }
441
442 #[derive(Debug, Error)]
451 pub struct UndeclaredActionsDescendantError {
452 pub(crate) euids: NonEmpty<EntityUID>,
454 }
455
456 impl Display for UndeclaredActionsDescendantError {
457 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
458 if self.euids.len() == 1 {
459 write!(f, "undeclared action: ")?;
460 } else {
461 write!(f, "undeclared actions: ")?;
462 }
463 join_with_conjunction(f, "and", self.euids.iter().sorted_unstable(), |f, s| {
464 s.fmt(f)
465 })
466 }
467 }
468
469 impl Diagnostic for UndeclaredActionsDescendantError {
470 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
471 Some(Box::new(
472 "any actions appearing as descendants need to be declared as actions in the schema",
473 ))
474 }
475
476 impl_diagnostic_from_method_on_nonempty_field!(euids, loc);
477 }
478
479 #[derive(Debug, Error)]
485 #[error("failed to resolve type{}: {}", if .undefined_types.len() > 1 { "s" } else { "" }, .undefined_types.iter().map(crate::validator::ConditionalName::raw).join(", "))]
486 pub struct TypeNotDefinedError {
487 pub(crate) undefined_types: NonEmpty<crate::validator::ConditionalName>,
489 }
490
491 impl Diagnostic for TypeNotDefinedError {
492 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
493 Some(Box::new(
495 self.undefined_types.first().resolution_failure_help(),
496 ))
497 }
498
499 impl_diagnostic_from_method_on_nonempty_field!(undefined_types, loc);
500 }
501
502 impl TypeNotDefinedError {
503 pub(crate) fn join_nonempty(errs: NonEmpty<TypeNotDefinedError>) -> Self {
508 Self {
509 undefined_types: errs.flat_map(|err| err.undefined_types),
510 }
511 }
512 }
513
514 impl From<NonEmpty<TypeNotDefinedError>> for TypeNotDefinedError {
515 fn from(value: NonEmpty<TypeNotDefinedError>) -> Self {
516 Self::join_nonempty(value)
517 }
518 }
519
520 #[derive(Debug, Diagnostic, Error)]
526 #[diagnostic(help("any actions appearing as parents need to be declared as actions"))]
527 pub struct ActionNotDefinedError(
528 pub(crate) NonEmpty<
529 crate::validator::json_schema::ActionEntityUID<crate::validator::ConditionalName>,
530 >,
531 );
532
533 impl ActionNotDefinedError {
534 pub(crate) fn join_nonempty(errs: NonEmpty<ActionNotDefinedError>) -> Self {
539 Self(errs.flat_map(|err| err.0))
540 }
541 }
542
543 impl From<NonEmpty<ActionNotDefinedError>> for ActionNotDefinedError {
544 fn from(value: NonEmpty<ActionNotDefinedError>) -> Self {
545 Self::join_nonempty(value)
546 }
547 }
548
549 impl Display for ActionNotDefinedError {
550 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
551 if self.0.len() == 1 {
552 write!(f, "undeclared action: ")?;
553 } else {
554 write!(f, "undeclared actions: ")?;
555 }
556 join_with_conjunction(
557 f,
558 "and",
559 self.0.iter().map(|aeuid| aeuid.as_raw()),
560 |f, s| s.fmt(f),
561 )
562 }
563 }
564
565 #[derive(Debug, Error)]
573 #[error(
574 "definition of `{shadowing_def}` illegally shadows the existing definition of `{shadowed_def}`"
575 )]
576 pub struct TypeShadowingError {
577 pub(crate) shadowed_def: InternalName,
579 pub(crate) shadowing_def: InternalName,
581 }
582
583 impl Diagnostic for TypeShadowingError {
584 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
585 Some(Box::new(format!(
586 "try renaming one of the definitions, or moving `{}` to a different namespace",
587 self.shadowed_def
588 )))
589 }
590
591 impl_diagnostic_from_method_on_field!(shadowing_def, loc);
594 }
595
596 #[derive(Debug, Error)]
604 #[error(
605 "definition of `{shadowing_def}` illegally shadows the existing definition of `{shadowed_def}`"
606 )]
607 pub struct ActionShadowingError {
608 pub(crate) shadowed_def: EntityUID,
610 pub(crate) shadowing_def: EntityUID,
612 }
613
614 impl Diagnostic for ActionShadowingError {
615 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
616 Some(Box::new(format!(
617 "try renaming one of the actions, or moving `{}` to a different namespace",
618 self.shadowed_def
619 )))
620 }
621
622 impl_diagnostic_from_method_on_field!(shadowing_def, loc);
625 }
626
627 #[derive(Debug, Error)]
633 #[error("duplicate entity type `{ty}`")]
634 pub struct DuplicateEntityTypeError {
635 pub(crate) ty: EntityType,
636 }
637
638 impl Diagnostic for DuplicateEntityTypeError {
639 impl_diagnostic_from_method_on_field!(ty, loc);
640 }
641
642 #[derive(Debug, Diagnostic, Error)]
648 #[error("duplicate action `{0}`")]
649 pub struct DuplicateActionError(pub(crate) SmolStr);
650
651 #[derive(Debug, Error)]
657 #[error("duplicate common type `{ty}`")]
658 pub struct DuplicateCommonTypeError {
659 pub(crate) ty: InternalName,
660 }
661
662 impl Diagnostic for DuplicateCommonTypeError {
663 impl_diagnostic_from_method_on_field!(ty, loc);
664 }
665
666 #[derive(Debug, Error)]
672 #[error("cycle in action hierarchy containing `{uid}`")]
673 pub struct CycleInActionHierarchyError {
674 pub(crate) uid: EntityUID,
675 }
676
677 impl Diagnostic for CycleInActionHierarchyError {
678 impl_diagnostic_from_method_on_field!(uid, loc);
679 }
680
681 #[derive(Debug, Error)]
687 #[error("cycle in common type references containing `{ty}`")]
688 pub struct CycleInCommonTypeReferencesError {
689 pub(crate) ty: InternalName,
690 }
691
692 impl Diagnostic for CycleInCommonTypeReferencesError {
693 impl_diagnostic_from_method_on_field!(ty, loc);
694 }
695
696 #[derive(Debug, Clone, Diagnostic, Error)]
702 #[error("entity type `Action` declared in `entityTypes` list")]
703 pub struct ActionEntityTypeDeclaredError {}
704
705 #[derive(Debug, Error)]
711 #[error("{ctx_or_shape} is declared with a type other than `Record`")]
712 pub struct ContextOrShapeNotRecordError {
713 pub(crate) ctx_or_shape: ContextOrShape,
714 }
715
716 impl Diagnostic for ContextOrShapeNotRecordError {
717 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
718 match &self.ctx_or_shape {
719 ContextOrShape::ActionContext(_) => {
720 Some(Box::new("action contexts must have type `Record`"))
721 }
722 ContextOrShape::EntityTypeShape(_) => {
723 Some(Box::new("entity type shapes must have type `Record`"))
724 }
725 }
726 }
727
728 impl_diagnostic_from_method_on_field!(ctx_or_shape, loc);
729 }
730
731 #[derive(Diagnostic, Debug, Error)]
737 #[error("internal invariant violated: this error is deprecated and should never be returned")]
738 #[deprecated = "this error is deprecated and should never be returned"]
739 pub struct ActionAttributesContainEmptySetError {}
740
741 #[derive(Diagnostic, Debug, Error)]
747 #[error("internal invariant violated: this error is deprecated and should never be returned")]
748 #[deprecated = "this error is deprecated and should never be returned"]
749 pub struct UnsupportedActionAttributeError {}
750
751 #[derive(Diagnostic, Debug, Error)]
757 #[error("internal invariant violated: this error is deprecated and should never be returned")]
758 #[deprecated = "this error is deprecated and should never be returned"]
759 pub struct ExprEscapeUsedError {}
760
761 #[derive(Diagnostic, Debug, Error)]
767 #[error("internal invariant violated: this error is deprecated and should never be returned")]
768 #[deprecated = "this error is deprecated and should never be returned"]
769 pub struct ActionAttrEvalError();
770
771 #[derive(Debug, Diagnostic, Error)]
777 #[error("unsupported feature used in schema")]
778 #[diagnostic(transparent)]
779 pub struct UnsupportedFeatureError(#[from] pub(crate) UnsupportedFeature);
780
781 #[derive(Debug)]
782 pub(crate) enum ContextOrShape {
783 ActionContext(EntityUID),
784 EntityTypeShape(EntityType),
785 }
786
787 impl ContextOrShape {
788 pub fn loc(&self) -> Option<&Loc> {
789 match self {
790 ContextOrShape::ActionContext(uid) => uid.loc(),
791 ContextOrShape::EntityTypeShape(ty) => ty.loc(),
792 }
793 }
794 }
795
796 impl std::fmt::Display for ContextOrShape {
797 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798 match self {
799 ContextOrShape::ActionContext(action) => write!(f, "Context for action {action}"),
800 ContextOrShape::EntityTypeShape(entity_type) => {
801 write!(f, "Shape for entity type {entity_type}")
802 }
803 }
804 }
805 }
806
807 #[derive(Debug, Diagnostic, Error)]
808 pub(crate) enum UnsupportedFeature {
809 #[error("records and entities with `additionalAttributes` are experimental, but the experimental `partial-validate` feature is not enabled")]
810 OpenRecordsAndEntities,
811 #[error("action declared with attributes: [{}]", .0.iter().join(", "))]
813 ActionAttributes(Vec<String>),
814 }
815
816 #[derive(Debug, Error)]
822 #[error("{err}")]
823 pub struct JsonDeserializationError {
824 err: serde_json::Error,
826 advice: Option<JsonDeserializationAdvice>,
828 }
829
830 impl Diagnostic for JsonDeserializationError {
831 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
832 self.advice
833 .as_ref()
834 .map(|h| Box::new(h) as Box<dyn Display>)
835 }
836 }
837
838 #[derive(Debug, Error)]
839 enum JsonDeserializationAdvice {
840 #[error("this API was expecting a schema in the JSON format; did you mean to use a different function, which expects the Cedar schema format?")]
841 CedarFormat,
842 #[error("JSON formatted schema must specify a namespace. If you want to use the empty namespace, explicitly specify it with `{{ \"\": {{..}} }}`")]
843 MissingNamespace,
844 }
845
846 impl JsonDeserializationError {
847 pub(crate) fn new(err: serde_json::Error, src: Option<&str>) -> Self {
851 match src {
852 None => Self { err, advice: None },
853 Some(src) => {
854 let advice = match src.trim_start().chars().next() {
856 None => None, Some('{') => {
858 if let Ok(serde_json::Value::Object(obj)) =
861 serde_json::from_str::<serde_json::Value>(src)
862 {
863 if obj.contains_key("entityTypes")
864 || obj.contains_key("actions")
865 || obj.contains_key("commonTypes")
866 {
867 Some(JsonDeserializationAdvice::MissingNamespace)
870 } else {
871 None
873 }
874 } else {
875 None
877 }
878 }
879 Some(_) => Some(JsonDeserializationAdvice::CedarFormat), };
881 Self { err, advice }
882 }
883 }
884 }
885 }
886
887 #[derive(Error, Debug)]
893 #[error("unknown extension type `{actual}`")]
894 pub struct UnknownExtensionTypeError {
895 pub(crate) actual: Name,
896 pub(crate) suggested_replacement: Option<String>,
897 }
898
899 impl Diagnostic for UnknownExtensionTypeError {
900 fn help<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
901 self.suggested_replacement.as_ref().map(|suggestion| {
902 Box::new(format!("did you mean `{suggestion}`?")) as Box<dyn Display>
903 })
904 }
905
906 impl_diagnostic_from_method_on_field!(actual, loc);
907 }
908
909 impl UnknownExtensionTypeError {
910 pub(crate) fn new_with_suggestion(actual: Name, extensions: &Extensions<'_>) -> Self {
911 let suggested_replacement = fuzzy_search(
912 &actual.to_string(),
913 &extensions
914 .ext_types()
915 .map(|n| n.to_string())
916 .collect::<Vec<_>>(),
917 );
918 UnknownExtensionTypeError {
919 actual,
920 suggested_replacement,
921 }
922 }
923 }
924
925 #[derive(Error, Debug)]
932 #[error("internal invariant violated: failed to find a common-type definition for {name}")]
933 pub struct CommonTypeInvariantViolationError {
934 pub(crate) name: InternalName,
936 }
937
938 impl Diagnostic for CommonTypeInvariantViolationError {
939 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
940 Some(Box::new("please file an issue at <https://github.com/cedar-policy/cedar/issues> including the schema that caused this error"))
941 }
942
943 impl_diagnostic_from_method_on_field!(name, loc);
944 }
945
946 #[derive(Error, Debug)]
953 #[error("internal invariant violated: failed to find {} for {}", if .euids.len() > 1 { "action definitions" } else { "an action definition" }, .euids.iter().join(", "))]
954 pub struct ActionInvariantViolationError {
955 pub(crate) euids: NonEmpty<EntityUID>,
957 }
958
959 impl Diagnostic for ActionInvariantViolationError {
960 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
961 Some(Box::new("please file an issue at <https://github.com/cedar-policy/cedar/issues> including the schema that caused this error"))
962 }
963
964 impl_diagnostic_from_method_on_nonempty_field!(euids, loc);
965 }
966
967 #[derive(Debug, Error)]
974 #[error("enum entity type `{enum_type}` cannot be a descendant of `{parent_type}`")]
975 pub struct EnumEntityInHierarchyError {
976 pub(crate) enum_type: EntityType,
978 pub(crate) parent_type: EntityType,
980 }
981
982 impl Diagnostic for EnumEntityInHierarchyError {
983 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
984 Some(Box::new(
985 "enum entity types cannot have parents in the entity hierarchy",
986 ))
987 }
988
989 impl_diagnostic_from_method_on_field!(parent_type, loc);
990 }
991
992 #[derive(Debug, Error)]
998 #[error("action `{uid}` has an entity type whose basename is not `Action`")]
999 pub struct InvalidActionTypeError {
1000 pub(crate) uid: EntityUID,
1001 }
1002
1003 impl Diagnostic for InvalidActionTypeError {
1004 impl_diagnostic_from_method_on_field!(uid, loc);
1005 }
1006}