Skip to main content

muffy_validation/
lib.rs

1//! Document validation.
2
3extern crate alloc;
4
5use alloc::collections::{BTreeMap, BTreeSet};
6use muffy_document::html::Element;
7use muffy_validation_macro::html;
8
9html! {}
10
11/// A validation error.
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub enum ValidationError {
14    /// An invalid tag.
15    InvalidTag(String),
16    /// Invalid element.
17    InvalidElement {
18        /// Invalid attributes by name.
19        attributes: BTreeMap<String, BTreeSet<AttributeError>>,
20        /// Invalid children by name.
21        children: BTreeMap<String, BTreeSet<ChildError>>,
22    },
23}
24
25/// A validation attribute error.
26#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
27pub enum AttributeError {
28    /// An invalid attribute.
29    Invalid,
30}
31
32/// A validation child error.
33#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
34pub enum ChildError {
35    /// An invalid child.
36    Invalid,
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use muffy_document::html::Node;
43    use std::sync::Arc;
44
45    fn create_element(
46        name: &str,
47        attributes: Vec<(&str, &str)>,
48        children: Vec<Element>,
49    ) -> Element {
50        Element::new(
51            name.to_owned(),
52            attributes
53                .into_iter()
54                .map(|(k, v)| (k.to_owned(), v.to_owned()))
55                .collect(),
56            children
57                .into_iter()
58                .map(|e| Arc::new(Node::Element(e)))
59                .collect(),
60        )
61    }
62
63    #[test]
64    fn validate_invalid_element_name() {
65        let element = create_element("invalid", vec![], vec![]);
66
67        assert_eq!(
68            validate_element(&element),
69            Err(ValidationError::InvalidTag("invalid".to_owned()))
70        );
71    }
72
73    mod div {
74        use super::*;
75
76        #[test]
77        fn validate_valid_element() {
78            let element = create_element("div", vec![], vec![]);
79
80            assert_eq!(validate_element(&element), Ok(()));
81        }
82
83        #[test]
84        fn validate_valid_attributes() {
85            let element = create_element("div", vec![("id", "foo"), ("class", "bar")], vec![]);
86
87            assert_eq!(validate_element(&element), Ok(()));
88        }
89
90        #[test]
91        fn validate_invalid_attribute() {
92            let element = create_element("div", vec![("invalid", "foo")], vec![]);
93
94            assert_eq!(
95                validate_element(&element),
96                Err(ValidationError::InvalidElement {
97                    attributes: [("invalid".into(), [AttributeError::Invalid].into())].into(),
98                    children: Default::default(),
99                })
100            );
101        }
102
103        #[test]
104        fn validate_multiple_invalid_attributes() {
105            let element = create_element(
106                "div",
107                vec![("invalid-one", "foo"), ("invalid-two", "bar")],
108                vec![],
109            );
110
111            assert_eq!(
112                validate_element(&element),
113                Err(ValidationError::InvalidElement {
114                    attributes: [
115                        ("invalid-one".into(), [AttributeError::Invalid].into()),
116                        ("invalid-two".into(), [AttributeError::Invalid].into()),
117                    ]
118                    .into(),
119                    children: Default::default(),
120                })
121            );
122        }
123
124        #[test]
125        fn validate_valid_child() {
126            let element = create_element("div", vec![], vec![create_element("p", vec![], vec![])]);
127
128            assert_eq!(validate_element(&element), Ok(()));
129        }
130    }
131
132    mod p {
133        use super::*;
134
135        #[test]
136        fn validate_valid_element() {
137            let element = create_element("p", vec![], vec![]);
138
139            assert_eq!(validate_element(&element), Ok(()));
140        }
141
142        #[test]
143        fn validate_invalid_child() {
144            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
145
146            assert_eq!(
147                validate_element(&element),
148                Err(ValidationError::InvalidElement {
149                    attributes: Default::default(),
150                    children: [("div".into(), [ChildError::Invalid].into())].into(),
151                })
152            );
153        }
154
155        #[test]
156        fn validate_multiple_invalid_children() {
157            let element = create_element(
158                "p",
159                vec![],
160                vec![
161                    create_element("div", vec![], vec![]),
162                    create_element("table", vec![], vec![]),
163                ],
164            );
165
166            assert_eq!(
167                validate_element(&element),
168                Err(ValidationError::InvalidElement {
169                    attributes: Default::default(),
170                    children: [
171                        ("div".into(), [ChildError::Invalid].into()),
172                        ("table".into(), [ChildError::Invalid].into()),
173                    ]
174                    .into(),
175                })
176            );
177        }
178    }
179
180    mod html {
181        use super::*;
182
183        #[test]
184        fn validate_valid_element() {
185            let element = create_element("html", vec![], vec![]);
186
187            assert_eq!(validate_element(&element), Ok(()));
188        }
189
190        #[test]
191        fn validate_valid_children() {
192            let element = create_element(
193                "html",
194                vec![],
195                vec![
196                    create_element("head", vec![], vec![]),
197                    create_element("body", vec![], vec![]),
198                ],
199            );
200
201            assert_eq!(validate_element(&element), Ok(()));
202        }
203    }
204
205    mod head {
206        use super::*;
207
208        #[test]
209        fn validate_valid_child() {
210            let element = create_element(
211                "head",
212                vec![],
213                vec![create_element("title", vec![], vec![])],
214            );
215
216            assert_eq!(validate_element(&element), Ok(()));
217        }
218
219        #[test]
220        fn validate_invalid_child() {
221            let element = create_element("head", vec![], vec![create_element("p", vec![], vec![])]);
222
223            assert_eq!(
224                validate_element(&element),
225                Err(ValidationError::InvalidElement {
226                    attributes: Default::default(),
227                    children: [("p".into(), [ChildError::Invalid].into())].into(),
228                })
229            );
230        }
231    }
232
233    mod title {
234        use super::*;
235
236        #[test]
237        fn validate_invalid_child() {
238            let element =
239                create_element("title", vec![], vec![create_element("div", vec![], vec![])]);
240
241            assert_eq!(
242                validate_element(&element),
243                Err(ValidationError::InvalidElement {
244                    attributes: Default::default(),
245                    children: [("div".into(), [ChildError::Invalid].into())].into(),
246                })
247            );
248        }
249    }
250
251    mod ul {
252        use super::*;
253
254        #[test]
255        fn validate_valid_child() {
256            let element = create_element("ul", vec![], vec![create_element("li", vec![], vec![])]);
257
258            assert_eq!(validate_element(&element), Ok(()));
259        }
260
261        #[test]
262        fn validate_invalid_child() {
263            let element = create_element("ul", vec![], vec![create_element("p", vec![], vec![])]);
264
265            assert_eq!(
266                validate_element(&element),
267                Err(ValidationError::InvalidElement {
268                    attributes: Default::default(),
269                    children: [("p".into(), [ChildError::Invalid].into())].into(),
270                })
271            );
272        }
273    }
274
275    mod table {
276        use super::*;
277
278        #[test]
279        fn validate_valid_child() {
280            let element =
281                create_element("table", vec![], vec![create_element("tr", vec![], vec![])]);
282
283            assert_eq!(validate_element(&element), Ok(()));
284        }
285
286        #[test]
287        fn validate_invalid_child() {
288            let element =
289                create_element("table", vec![], vec![create_element("p", vec![], vec![])]);
290
291            assert_eq!(
292                validate_element(&element),
293                Err(ValidationError::InvalidElement {
294                    attributes: Default::default(),
295                    children: [("p".into(), [ChildError::Invalid].into())].into(),
296                })
297            );
298        }
299    }
300
301    mod tr {
302        use super::*;
303
304        #[test]
305        fn validate_valid_children() {
306            let element = create_element(
307                "tr",
308                vec![],
309                vec![
310                    create_element("th", vec![], vec![]),
311                    create_element("td", vec![], vec![]),
312                ],
313            );
314
315            assert_eq!(validate_element(&element), Ok(()));
316        }
317    }
318
319    mod form {
320        use super::*;
321
322        #[test]
323        fn validate_valid_attributes() {
324            let element = create_element("form", vec![("action", "/"), ("method", "post")], vec![]);
325
326            assert_eq!(validate_element(&element), Ok(()));
327        }
328
329        #[test]
330        fn validate_valid_child() {
331            let element = create_element(
332                "form",
333                vec![],
334                vec![create_element("input", vec![], vec![])],
335            );
336
337            assert_eq!(validate_element(&element), Ok(()));
338        }
339    }
340
341    mod img {
342        use super::*;
343
344        #[test]
345        fn validate_valid_attributes() {
346            let element = create_element(
347                "img",
348                vec![("src", "img.png"), ("alt", "description")],
349                vec![],
350            );
351
352            assert_eq!(validate_element(&element), Ok(()));
353        }
354    }
355
356    mod video {
357        use super::*;
358
359        #[test]
360        fn validate_valid_attributes() {
361            let element =
362                create_element("video", vec![("src", "vid.mp4"), ("controls", "")], vec![]);
363
364            assert_eq!(validate_element(&element), Ok(()));
365        }
366
367        #[test]
368        fn validate_valid_child() {
369            let element = create_element(
370                "video",
371                vec![],
372                vec![create_element("track", vec![], vec![])],
373            );
374
375            assert_eq!(validate_element(&element), Ok(()));
376        }
377    }
378
379    mod meta {
380        use super::*;
381
382        #[test]
383        fn validate_valid_name_content() {
384            let element = create_element(
385                "meta",
386                vec![("name", "description"), ("content", "stuff")],
387                vec![],
388            );
389
390            assert_eq!(validate_element(&element), Ok(()));
391        }
392
393        #[test]
394        fn validate_valid_charset() {
395            let element = create_element("meta", vec![("charset", "utf-8")], vec![]);
396
397            assert_eq!(validate_element(&element), Ok(()));
398        }
399    }
400
401    mod link {
402        use super::*;
403
404        #[test]
405        fn validate_valid_attributes() {
406            let element = create_element(
407                "link",
408                vec![("rel", "stylesheet"), ("href", "style.css")],
409                vec![],
410            );
411
412            assert_eq!(validate_element(&element), Ok(()));
413        }
414    }
415}