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 help: Option<String>,
351 ) -> Self {
352 validation_errors::FunctionArgumentValidation {
353 source_loc,
354 policy_id,
355 msg,
356 help,
357 }
358 .into()
359 }
360
361 pub(crate) fn empty_set_forbidden(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
362 validation_errors::EmptySetForbidden {
363 source_loc,
364 policy_id,
365 }
366 .into()
367 }
368
369 pub(crate) fn non_lit_ext_constructor(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
370 validation_errors::NonLitExtConstructor {
371 source_loc,
372 policy_id,
373 }
374 .into()
375 }
376
377 pub(crate) fn internal_invariant_violation(
378 source_loc: Option<Loc>,
379 policy_id: PolicyID,
380 ) -> Self {
381 validation_errors::InternalInvariantViolation {
382 source_loc,
383 policy_id,
384 }
385 .into()
386 }
387
388 pub(crate) fn invalid_enum_entity(
389 source_loc: Option<Loc>,
390 policy_id: PolicyID,
391 err: InvalidEnumEntityError,
392 ) -> Self {
393 validation_errors::InvalidEnumEntity {
394 source_loc,
395 policy_id,
396 err,
397 }
398 .into()
399 }
400
401 pub(crate) fn maximum_level_exceeded(
402 source_loc: Option<Loc>,
403 policy_id: PolicyID,
404 allowed_level: crate::validator::level_validate::EntityDerefLevel,
405 actual_level: crate::validator::level_validate::EntityDerefLevel,
406 ) -> Self {
407 validation_errors::EntityDerefLevelViolation {
408 source_loc,
409 policy_id,
410 violation_kind: validation_errors::EntityDerefViolationKind::MaximumLevelExceeded {
411 allowed_level,
412 actual_level,
413 },
414 }
415 .into()
416 }
417
418 pub(crate) fn literal_dereference_target(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
419 validation_errors::EntityDerefLevelViolation {
420 source_loc,
421 policy_id,
422 violation_kind: validation_errors::EntityDerefViolationKind::LiteralDerefTarget,
423 }
424 .into()
425 }
426}
427
428#[derive(Debug, Clone, PartialEq, Diagnostic, Error, Eq, Hash)]
431pub enum ValidationWarning {
432 #[diagnostic(transparent)]
434 #[error(transparent)]
435 MixedScriptString(#[from] validation_warnings::MixedScriptString),
436 #[diagnostic(transparent)]
438 #[error(transparent)]
439 BidiCharsInString(#[from] validation_warnings::BidiCharsInString),
440 #[diagnostic(transparent)]
442 #[error(transparent)]
443 BidiCharsInIdentifier(#[from] validation_warnings::BidiCharsInIdentifier),
444 #[diagnostic(transparent)]
446 #[error(transparent)]
447 MixedScriptIdentifier(#[from] validation_warnings::MixedScriptIdentifier),
448 #[diagnostic(transparent)]
450 #[error(transparent)]
451 ConfusableIdentifier(#[from] validation_warnings::ConfusableIdentifier),
452 #[diagnostic(transparent)]
454 #[error(transparent)]
455 ImpossiblePolicy(#[from] validation_warnings::ImpossiblePolicy),
456}
457
458impl ValidationWarning {
459 pub(crate) fn mixed_script_string(
460 source_loc: Option<Loc>,
461 policy_id: PolicyID,
462 string: impl Into<String>,
463 ) -> Self {
464 validation_warnings::MixedScriptString {
465 source_loc,
466 policy_id,
467 string: string.into(),
468 }
469 .into()
470 }
471
472 pub(crate) fn bidi_chars_strings(
473 source_loc: Option<Loc>,
474 policy_id: PolicyID,
475 string: impl Into<String>,
476 ) -> Self {
477 validation_warnings::BidiCharsInString {
478 source_loc,
479 policy_id,
480 string: string.into(),
481 }
482 .into()
483 }
484
485 pub(crate) fn mixed_script_identifier(
486 source_loc: Option<Loc>,
487 policy_id: PolicyID,
488 id: impl Into<String>,
489 ) -> Self {
490 validation_warnings::MixedScriptIdentifier {
491 source_loc,
492 policy_id,
493 id: id.into(),
494 }
495 .into()
496 }
497
498 pub(crate) fn bidi_chars_identifier(
499 source_loc: Option<Loc>,
500 policy_id: PolicyID,
501 id: impl Into<String>,
502 ) -> Self {
503 validation_warnings::BidiCharsInIdentifier {
504 source_loc,
505 policy_id,
506 id: id.into(),
507 }
508 .into()
509 }
510
511 pub(crate) fn confusable_identifier(
512 source_loc: Option<Loc>,
513 policy_id: PolicyID,
514 id: impl Into<String>,
515 confusable_character: char,
516 ) -> Self {
517 validation_warnings::ConfusableIdentifier {
518 source_loc,
519 policy_id,
520 id: id.into(),
521 confusable_character,
522 }
523 .into()
524 }
525
526 pub(crate) fn impossible_policy(source_loc: Option<Loc>, policy_id: PolicyID) -> Self {
527 validation_warnings::ImpossiblePolicy {
528 source_loc,
529 policy_id,
530 }
531 .into()
532 }
533}