wdl_core/concern/
validation.rs1use std::collections::VecDeque;
4
5use convert_case::Case;
6use convert_case::Casing;
7use nonempty::NonEmpty;
8
9use crate::concern::Code;
10use crate::file::location;
11
12pub mod failure;
13
14pub use failure::Failure;
15
16#[derive(Debug)]
18pub enum Error {
19 Location(location::Error),
21}
22
23impl std::fmt::Display for Error {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Error::Location(err) => write!(f, "location error: {err}"),
27 }
28 }
29}
30
31impl std::error::Error for Error {}
32
33pub type Result = std::result::Result<Option<NonEmpty<Failure>>, Error>;
35
36#[derive(Debug)]
38pub struct Validator;
39
40impl Validator {
41 pub fn validate<'a, E>(tree: &'a E, rules: Vec<Box<dyn Rule<&'a E>>>) -> Result {
43 let mut failures = rules
44 .iter()
45 .map(|rule| rule.validate(tree))
46 .collect::<std::result::Result<Vec<Option<NonEmpty<Failure>>>, Error>>()?
47 .into_iter()
48 .flatten()
49 .flatten()
50 .collect::<VecDeque<Failure>>();
51
52 match failures.pop_front() {
53 Some(front) => {
54 let mut result = NonEmpty::new(front);
55 result.extend(failures);
56 Ok(Some(result))
57 }
58 None => Ok(None),
59 }
60 }
61}
62
63pub trait Rule<E>: std::fmt::Debug + Sync {
65 fn name(&self) -> String {
70 format!("{:?}", self).to_case(Case::Snake)
71 }
72
73 fn code(&self) -> Code;
75
76 fn validate(&self, tree: E) -> Result;
78}