Skip to main content

muffy_validation/
error.rs

1use alloc::collections::{BTreeMap, BTreeSet};
2use core::fmt::{self, Display, Formatter};
3
4/// A validation error.
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub enum ValidationError {
7    /// An unknown tag.
8    UnknownTag(String),
9    /// Invalid element.
10    InvalidElement {
11        /// Invalid attributes.
12        attributes: BTreeMap<String, BTreeSet<AttributeError>>,
13        /// Invalid children.
14        children: BTreeMap<String, BTreeSet<ChildError>>,
15    },
16}
17
18impl Display for ValidationError {
19    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
20        match self {
21            Self::UnknownTag(tag) => write!(formatter, "unknown tag \"{tag}\""),
22            Self::InvalidElement {
23                attributes,
24                children,
25            } => {
26                if !attributes.is_empty() {
27                    write!(formatter, "invalid attributes: ")?;
28
29                    for (index, (name, errors)) in attributes.iter().enumerate() {
30                        if index > 0 {
31                            write!(formatter, ", ")?;
32                        }
33
34                        write!(formatter, "{name} (")?;
35
36                        for (index, error) in errors.iter().enumerate() {
37                            if index > 0 {
38                                write!(formatter, ", ")?;
39                            }
40
41                            write!(formatter, "{error}")?;
42                        }
43
44                        write!(formatter, ")")?;
45                    }
46                }
47
48                if !children.is_empty() {
49                    if !attributes.is_empty() {
50                        write!(formatter, ", ")?;
51                    }
52
53                    write!(formatter, "invalid children: ")?;
54
55                    for (index, (name, errors)) in children.iter().enumerate() {
56                        if index > 0 {
57                            write!(formatter, ", ")?;
58                        }
59
60                        write!(formatter, "{name} (")?;
61
62                        for (index, error) in errors.iter().enumerate() {
63                            if index > 0 {
64                                write!(formatter, ", ")?;
65                            }
66
67                            write!(formatter, "{error}")?;
68                        }
69
70                        write!(formatter, ")")?;
71                    }
72                }
73
74                Ok(())
75            }
76        }
77    }
78}
79
80/// An attribute validation error.
81#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
82pub enum AttributeError {
83    /// Not allowed.
84    NotAllowed,
85}
86
87impl Display for AttributeError {
88    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
89        match self {
90            Self::NotAllowed => write!(formatter, "not allowed"),
91        }
92    }
93}
94
95/// A child validation error.
96#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
97pub enum ChildError {
98    /// Not allowed.
99    NotAllowed,
100}
101
102impl Display for ChildError {
103    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
104        match self {
105            Self::NotAllowed => write!(formatter, "not allowed"),
106        }
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn display_unknown_tag() {
116        assert_eq!(
117            format!("{}", ValidationError::UnknownTag("foo".into())),
118            "unknown tag \"foo\""
119        );
120    }
121
122    #[test]
123    fn display_not_allowed_attributes() {
124        assert_eq!(
125            format!(
126                "{}",
127                ValidationError::InvalidElement {
128                    attributes: [("foo".into(), [AttributeError::NotAllowed].into())].into(),
129                    children: Default::default(),
130                }
131            ),
132            "invalid attributes: foo (not allowed)"
133        );
134    }
135
136    #[test]
137    fn display_not_allowed_children() {
138        assert_eq!(
139            format!(
140                "{}",
141                ValidationError::InvalidElement {
142                    attributes: Default::default(),
143                    children: [("foo".into(), [ChildError::NotAllowed].into())].into(),
144                }
145            ),
146            "invalid children: foo (not allowed)"
147        );
148    }
149
150    #[test]
151    fn display_not_allowed_attributes_and_children() {
152        assert_eq!(
153            format!(
154                "{}",
155                ValidationError::InvalidElement {
156                    attributes: [("foo".into(), [AttributeError::NotAllowed].into())].into(),
157                    children: [("bar".into(), [ChildError::NotAllowed].into())].into(),
158                }
159            ),
160            "invalid attributes: foo (not allowed), invalid children: bar (not allowed)"
161        );
162    }
163}