cedar_policy_validator/
validation_result.rs1use cedar_policy_core::{ast::PolicyID, parser::SourceInfo};
18use thiserror::Error;
19
20use crate::{TypeErrorKind, ValidationWarning};
21
22#[derive(Debug)]
27pub struct ValidationResult<'a> {
28 validation_errors: Vec<ValidationError<'a>>,
29 validation_warnings: Vec<ValidationWarning<'a>>,
30}
31
32impl<'a> ValidationResult<'a> {
33 pub fn new(
34 errors: impl IntoIterator<Item = ValidationError<'a>>,
35 warnings: impl IntoIterator<Item = ValidationWarning<'a>>,
36 ) -> Self {
37 Self {
38 validation_errors: errors.into_iter().collect(),
39 validation_warnings: warnings.into_iter().collect(),
40 }
41 }
42
43 pub fn validation_passed(&self) -> bool {
46 self.validation_errors.is_empty()
47 }
48
49 pub fn validation_errors(&self) -> impl Iterator<Item = &ValidationError> {
51 self.validation_errors.iter()
52 }
53
54 pub fn validation_warnings(&self) -> impl Iterator<Item = &ValidationWarning> {
56 self.validation_warnings.iter()
57 }
58
59 pub fn into_errors_and_warnings(
61 self,
62 ) -> (
63 impl Iterator<Item = ValidationError<'a>>,
64 impl Iterator<Item = ValidationWarning<'a>>,
65 ) {
66 (
67 self.validation_errors.into_iter(),
68 self.validation_warnings.into_iter(),
69 )
70 }
71}
72
73#[derive(Debug)]
78#[cfg_attr(test, derive(Eq, PartialEq))]
79pub struct ValidationError<'a> {
80 location: SourceLocation<'a>,
81 error_kind: ValidationErrorKind,
82}
83
84impl<'a> ValidationError<'a> {
85 pub(crate) fn with_policy_id(
86 id: &'a PolicyID,
87 source_info: Option<SourceInfo>,
88 error_kind: ValidationErrorKind,
89 ) -> Self {
90 Self {
91 error_kind,
92 location: SourceLocation::new(id, source_info),
93 }
94 }
95
96 pub fn into_location_and_error_kind(self) -> (SourceLocation<'a>, ValidationErrorKind) {
98 (self.location, self.error_kind)
99 }
100
101 pub fn error_kind(&self) -> &ValidationErrorKind {
103 &self.error_kind
104 }
105
106 pub fn location(&self) -> &SourceLocation {
108 &self.location
109 }
110}
111
112#[derive(Debug, Clone, Eq, PartialEq)]
114pub struct SourceLocation<'a> {
115 policy_id: &'a PolicyID,
116 source_info: Option<SourceInfo>,
117}
118
119impl<'a> SourceLocation<'a> {
120 pub(crate) fn new(policy_id: &'a PolicyID, source_info: Option<SourceInfo>) -> Self {
121 Self {
122 policy_id,
123 source_info,
124 }
125 }
126
127 pub fn policy_id(&self) -> &'a PolicyID {
129 self.policy_id
130 }
131
132 pub fn source_info(&self) -> &Option<SourceInfo> {
133 &self.source_info
134 }
135
136 pub fn into_source_info(self) -> Option<SourceInfo> {
137 self.source_info
138 }
139}
140
141#[derive(Debug, Error)]
144#[cfg_attr(test, derive(Eq, PartialEq))]
145#[non_exhaustive]
146pub enum ValidationErrorKind {
147 #[error(
149 "unrecognized entity type `{}`{}",
150 .0.actual_entity_type,
151 match &.0.suggested_entity_type {
152 Some(s) => format!(", did you mean `{}`?", s),
153 None => "".to_string()
154 }
155 )]
156 UnrecognizedEntityType(UnrecognizedEntityType),
157 #[error(
159 "unrecognized action `{}`{}",
160 .0.actual_action_id,
161 match &.0.suggested_action_id {
162 Some(s) => format!(", did you mean `{}`?", s),
163 None => "".to_string()
164 }
165 )]
166 UnrecognizedActionId(UnrecognizedActionId),
167 #[error(
171 "unable to find an applicable action given the policy head constraints{}{}",
172 if .0.would_in_fix_principal { ". Note: Try replacing `==` with `in` in the principal clause" } else { "" },
173 if .0.would_in_fix_resource { ". Note: Try replacing `==` with `in` in the resource clause" } else { "" }
174 )]
175 InvalidActionApplication(InvalidActionApplication),
176 #[error(transparent)]
178 TypeError(TypeErrorKind),
179 #[error(
182 "unspecified entity with eid `{}`. Unspecified entities cannot be used in policies",
183 .0.entity_id,
184 )]
185 UnspecifiedEntity(UnspecifiedEntity),
186}
187
188impl ValidationErrorKind {
189 pub(crate) fn unrecognized_entity_type(
190 actual_entity_type: String,
191 suggested_entity_type: Option<String>,
192 ) -> ValidationErrorKind {
193 Self::UnrecognizedEntityType(UnrecognizedEntityType {
194 actual_entity_type,
195 suggested_entity_type,
196 })
197 }
198
199 pub(crate) fn unrecognized_action_id(
200 actual_action_id: String,
201 suggested_action_id: Option<String>,
202 ) -> ValidationErrorKind {
203 Self::UnrecognizedActionId(UnrecognizedActionId {
204 actual_action_id,
205 suggested_action_id,
206 })
207 }
208
209 pub(crate) fn invalid_action_application(
210 would_in_fix_principal: bool,
211 would_in_fix_resource: bool,
212 ) -> ValidationErrorKind {
213 Self::InvalidActionApplication(InvalidActionApplication {
214 would_in_fix_principal,
215 would_in_fix_resource,
216 })
217 }
218
219 pub(crate) fn type_error(type_error: TypeErrorKind) -> ValidationErrorKind {
220 Self::TypeError(type_error)
221 }
222
223 pub(crate) fn unspecified_entity(entity_id: String) -> ValidationErrorKind {
224 Self::UnspecifiedEntity(UnspecifiedEntity { entity_id })
225 }
226}
227
228#[derive(Debug)]
230#[cfg_attr(test, derive(Eq, PartialEq))]
231pub struct UnrecognizedEntityType {
232 pub(crate) actual_entity_type: String,
234 pub(crate) suggested_entity_type: Option<String>,
237}
238
239#[derive(Debug)]
241#[cfg_attr(test, derive(Eq, PartialEq))]
242pub struct UnrecognizedActionId {
243 pub(crate) actual_action_id: String,
245 pub(crate) suggested_action_id: Option<String>,
248}
249
250#[derive(Debug)]
252#[cfg_attr(test, derive(Eq, PartialEq))]
253pub struct InvalidActionApplication {
254 pub(crate) would_in_fix_principal: bool,
255 pub(crate) would_in_fix_resource: bool,
256}
257
258#[derive(Debug)]
260#[cfg_attr(test, derive(Eq, PartialEq))]
261pub struct UnspecifiedEntity {
262 pub(crate) entity_id: String,
264}