1use crate::entities::conformance::err::InvalidEnumEntityError;
21use crate::parser::Loc;
22use miette::Diagnostic;
23use thiserror::Error;
24use validation_errors::UnrecognizedActionIdHelp;
25
26use std::collections::BTreeSet;
27
28use crate::ast::{EntityType, Expr, PolicyID};
29
30use crate::validator::types::{EntityLUB, Type};
31
32pub mod validation_errors;
33pub mod validation_warnings;
34
35#[derive(Debug)]
40pub struct ValidationResult {
41 validation_errors: Vec<ValidationError>,
42 validation_warnings: Vec<ValidationWarning>,
43}
44
45impl ValidationResult {
46 pub fn new(
49 errors: impl IntoIterator<Item = ValidationError>,
50 warnings: impl IntoIterator<Item = ValidationWarning>,
51 ) -> Self {
52 Self {
53 validation_errors: errors.into_iter().collect(),
54 validation_warnings: warnings.into_iter().collect(),
55 }
56 }
57
58 pub fn validation_passed(&self) -> bool {
61 self.validation_errors.is_empty()
62 }
63
64 pub fn validation_errors(&self) -> impl Iterator<Item = &ValidationError> {
66 self.validation_errors.iter()
67 }
68
69 pub fn validation_warnings(&self) -> impl Iterator<Item = &ValidationWarning> {
71 self.validation_warnings.iter()
72 }
73
74 pub fn into_errors_and_warnings(
76 self,
77 ) -> (
78 impl Iterator<Item = ValidationError>,
79 impl Iterator<Item = ValidationWarning>,
80 ) {
81 (
82 self.validation_errors.into_iter(),
83 self.validation_warnings.into_iter(),
84 )
85 }
86}
87
88#[derive(Clone, Debug, Diagnostic, Error, Hash, Eq, PartialEq)]
95pub enum ValidationError {
96 #[error(transparent)]
98 #[diagnostic(transparent)]
99 UnrecognizedEntityType(#[from] validation_errors::UnrecognizedEntityType),
100 #[error(transparent)]
102 #[diagnostic(transparent)]
103 UnrecognizedActionId(#[from] validation_errors::UnrecognizedActionId),
104 #[error(transparent)]
108 #[diagnostic(transparent)]
109 InvalidActionApplication(#[from] validation_errors::InvalidActionApplication),
110 #[error(transparent)]
113 #[diagnostic(transparent)]
114 UnexpectedType(#[from] validation_errors::UnexpectedType),
115 #[error(transparent)]
117 #[diagnostic(transparent)]
118 IncompatibleTypes(#[from] validation_errors::IncompatibleTypes),
119 #[error(transparent)]
122 #[diagnostic(transparent)]
123 UnsafeAttributeAccess(#[from] validation_errors::UnsafeAttributeAccess),
124 #[error(transparent)]
127 #[diagnostic(transparent)]
128 UnsafeOptionalAttributeAccess(#[from] validation_errors::UnsafeOptionalAttributeAccess),
129 #[error(transparent)]
131 #[diagnostic(transparent)]
132 UnsafeTagAccess(#[from] validation_errors::UnsafeTagAccess),
133 #[error(transparent)]
135 #[diagnostic(transparent)]
136 NoTagsAllowed(#[from] validation_errors::NoTagsAllowed),
137 #[error(transparent)]
139 #[diagnostic(transparent)]
140 UndefinedFunction(#[from] validation_errors::UndefinedFunction),
141 #[error(transparent)]
143 #[diagnostic(transparent)]
144 WrongNumberArguments(#[from] validation_errors::WrongNumberArguments),
145 #[diagnostic(transparent)]
148 #[error(transparent)]
149 FunctionArgumentValidation(#[from] validation_errors::FunctionArgumentValidation),
150 #[diagnostic(transparent)]
152 #[error(transparent)]
153 EmptySetForbidden(#[from] validation_errors::EmptySetForbidden),
154 #[diagnostic(transparent)]
157 #[error(transparent)]
158 NonLitExtConstructor(#[from] validation_errors::NonLitExtConstructor),
159 #[error(transparent)]
162 #[diagnostic(transparent)]
163 InternalInvariantViolation(#[from] validation_errors::InternalInvariantViolation),
164 #[error(transparent)]
167 #[diagnostic(transparent)]
168 InvalidEnumEntity(#[from] validation_errors::InvalidEnumEntity),
169 #[error(transparent)]
172 #[diagnostic(transparent)]
173 EntityDerefLevelViolation(#[from] validation_errors::EntityDerefLevelViolation),
174}
175
176impl ValidationError {
177 pub(crate) fn unrecognized_entity_type(
178 source_loc: Option<Loc>,
179 policy_id: PolicyID,
180 actual_entity_type: String,
181 suggested_entity_type: Option<String>,
182 ) -> Self {
183 validation_errors::UnrecognizedEntityType {
184 source_loc,
185 policy_id,
186 actual_entity_type,
187 suggested_entity_type,
188 }
189 .into()
190 }
191
192 pub(crate) fn unrecognized_action_id(
193 source_loc: Option<Loc>,
194
195 policy_id: PolicyID,
196 actual_action_id: String,
197 hint: Option<UnrecognizedActionIdHelp>,
198 ) -> Self {
199 validation_errors::UnrecognizedActionId {
200 source_loc,
201 policy_id,
202 actual_action_id,
203 hint,
204 }
205 .into()
206 }
207
208 pub(crate) fn invalid_action_application(
209 source_loc: Option<Loc>,
210 policy_id: PolicyID,
211 would_in_fix_principal: bool,
212 would_in_fix_resource: bool,
213 ) -> Self {
214 validation_errors::InvalidActionApplication {
215 source_loc,
216 policy_id,
217 would_in_fix_principal,
218 would_in_fix_resource,
219 }
220 .into()
221 }
222
223 pub(crate) fn expected_one_of_types(
225 source_loc: Option<Loc>,
226 policy_id: PolicyID,
227 expected: Vec<Type>,
228 actual: Type,
229 help: Option<validation_errors::UnexpectedTypeHelp>,
230 ) -> Self {
231 validation_errors::UnexpectedType {
232 source_loc,
233 policy_id,
234 expected,
235 actual,
236 help,
237 }
238 .into()
239 }
240
241 pub(crate) fn incompatible_types(
244 source_loc: Option<Loc>,
245 policy_id: PolicyID,
246 types: impl IntoIterator<Item = Type>,
247 hint: validation_errors::LubHelp,
248 context: validation_errors::LubContext,
249 ) -> Self {
250 validation_errors::IncompatibleTypes {
251 source_loc,
252 policy_id,
253 types: types.into_iter().collect::<BTreeSet<_>>(),
254 hint,
255 context,
256 }
257 .into()
258 }
259
260 pub(crate) fn unsafe_attribute_access(
261 source_loc: Option<Loc>,
262 policy_id: PolicyID,
263 attribute_access: validation_errors::AttributeAccess,
264 suggestion: Option<String>,
265 may_exist: bool,
266 ) -> Self {
267 validation_errors::UnsafeAttributeAccess {
268 source_loc,
269 policy_id,
270 attribute_access,
271 suggestion,
272 may_exist,
273 }
274 .into()
275 }
276
277 pub(crate) fn unsafe_optional_attribute_access(
278 source_loc: Option<Loc>,
279 policy_id: PolicyID,
280 attribute_access: validation_errors::AttributeAccess,
281 ) -> Self {
282 validation_errors::UnsafeOptionalAttributeAccess {
283 source_loc,
284 policy_id,
285 attribute_access,
286 }
287 .into()
288 }
289
290 pub(crate) fn unsafe_tag_access(
291 source_loc: Option<Loc>,
292 policy_id: PolicyID,
293 entity_ty: Option<EntityLUB>,
294 tag: Expr<Option<Type>>,
295 ) -> Self {
296 validation_errors::UnsafeTagAccess {
297 source_loc,
298 policy_id,
299 entity_ty,
300 tag,
301 }
302 .into()
303 }
304
305 pub(crate) fn no_tags_allowed(
306 source_loc: Option<Loc>,
307 policy_id: PolicyID,
308 entity_ty: Option<EntityType>,
309 ) -> Self {
310 validation_errors::NoTagsAllowed {
311 source_loc,
312 policy_id,
313 entity_ty,
314 }
315 .into()
316 }
317
318 pub(crate) fn undefined_extension(
319 source_loc: Option<Loc>,
320 policy_id: PolicyID,
321 name: String,
322 ) -> Self {
323 validation_errors::UndefinedFunction {
324 source_loc,
325 policy_id,
326 name,
327 }
328 .into()
329 }
330
331 pub(crate) fn wrong_number_args(
332 source_loc: Option<Loc>,
333 policy_id: PolicyID,
334 expected: usize,
335 actual: usize,
336 ) -> Self {
337 validation_errors::WrongNumberArguments {
338 source_loc,
339 policy_id,
340 expected,
341 actual,
342 }
343 .into()
344 }
345
346 pub(crate) fn function_argument_validation(
347 source_loc: Option<Loc>,
348 policy_id: PolicyID,
349 msg: String,
350 ) -> Self {
351 validation_errors::FunctionArgumentValidation {
352 source_loc,
353 policy_id,
354 msg,
355 }
356 .into()
357 }
358
359 pub(crate) fn empty_set_forbidden(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
360 validation_errors::EmptySetForbidden {
361 source_loc,
362 policy_id,
363 }
364 .into()
365 }
366
367 pub(crate) fn non_lit_ext_constructor(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
368 validation_errors::NonLitExtConstructor {
369 source_loc,
370 policy_id,
371 }
372 .into()
373 }
374
375 pub(crate) fn internal_invariant_violation(
376 source_loc: Option<Loc>,
377 policy_id: PolicyID,
378 ) -> Self {
379 validation_errors::InternalInvariantViolation {
380 source_loc,
381 policy_id,
382 }
383 .into()
384 }
385
386 pub(crate) fn invalid_enum_entity(
387 source_loc: Option<Loc>,
388 policy_id: PolicyID,
389 err: InvalidEnumEntityError,
390 ) -> Self {
391 validation_errors::InvalidEnumEntity {
392 source_loc,
393 policy_id,
394 err,
395 }
396 .into()
397 }
398
399 pub(crate) fn maximum_level_exceeded(
400 source_loc: Option<Loc>,
401 policy_id: PolicyID,
402 allowed_level: crate::validator::level_validate::EntityDerefLevel,
403 actual_level: crate::validator::level_validate::EntityDerefLevel,
404 ) -> Self {
405 validation_errors::EntityDerefLevelViolation {
406 source_loc,
407 policy_id,
408 violation_kind: validation_errors::EntityDerefViolationKind::MaximumLevelExceeded {
409 allowed_level,
410 actual_level,
411 },
412 }
413 .into()
414 }
415
416 pub(crate) fn literal_dereference_target(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
417 validation_errors::EntityDerefLevelViolation {
418 source_loc,
419 policy_id,
420 violation_kind: validation_errors::EntityDerefViolationKind::LiteralDerefTarget,
421 }
422 .into()
423 }
424}
425
426#[derive(Debug, Clone, PartialEq, Diagnostic, Error, Eq, Hash)]
429pub enum ValidationWarning {
430 #[diagnostic(transparent)]
432 #[error(transparent)]
433 MixedScriptString(#[from] validation_warnings::MixedScriptString),
434 #[diagnostic(transparent)]
436 #[error(transparent)]
437 BidiCharsInString(#[from] validation_warnings::BidiCharsInString),
438 #[diagnostic(transparent)]
440 #[error(transparent)]
441 BidiCharsInIdentifier(#[from] validation_warnings::BidiCharsInIdentifier),
442 #[diagnostic(transparent)]
444 #[error(transparent)]
445 MixedScriptIdentifier(#[from] validation_warnings::MixedScriptIdentifier),
446 #[diagnostic(transparent)]
448 #[error(transparent)]
449 ConfusableIdentifier(#[from] validation_warnings::ConfusableIdentifier),
450 #[diagnostic(transparent)]
452 #[error(transparent)]
453 ImpossiblePolicy(#[from] validation_warnings::ImpossiblePolicy),
454}
455
456impl ValidationWarning {
457 pub(crate) fn mixed_script_string(
458 source_loc: Option<Loc>,
459 policy_id: PolicyID,
460 string: impl Into<String>,
461 ) -> Self {
462 validation_warnings::MixedScriptString {
463 source_loc,
464 policy_id,
465 string: string.into(),
466 }
467 .into()
468 }
469
470 pub(crate) fn bidi_chars_strings(
471 source_loc: Option<Loc>,
472 policy_id: PolicyID,
473 string: impl Into<String>,
474 ) -> Self {
475 validation_warnings::BidiCharsInString {
476 source_loc,
477 policy_id,
478 string: string.into(),
479 }
480 .into()
481 }
482
483 pub(crate) fn mixed_script_identifier(
484 source_loc: Option<Loc>,
485 policy_id: PolicyID,
486 id: impl Into<String>,
487 ) -> Self {
488 validation_warnings::MixedScriptIdentifier {
489 source_loc,
490 policy_id,
491 id: id.into(),
492 }
493 .into()
494 }
495
496 pub(crate) fn bidi_chars_identifier(
497 source_loc: Option<Loc>,
498 policy_id: PolicyID,
499 id: impl Into<String>,
500 ) -> Self {
501 validation_warnings::BidiCharsInIdentifier {
502 source_loc,
503 policy_id,
504 id: id.into(),
505 }
506 .into()
507 }
508
509 pub(crate) fn confusable_identifier(
510 source_loc: Option<Loc>,
511 policy_id: PolicyID,
512 id: impl Into<String>,
513 confusable_character: char,
514 ) -> Self {
515 validation_warnings::ConfusableIdentifier {
516 source_loc,
517 policy_id,
518 id: id.into(),
519 confusable_character,
520 }
521 .into()
522 }
523
524 pub(crate) fn impossible_policy(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
525 validation_warnings::ImpossiblePolicy {
526 source_loc,
527 policy_id,
528 }
529 .into()
530 }
531}