shine_gltf/
validation.rs

1use crate::{Path, Root};
2use serde::ser;
3use serde::{Serialize, Serializer};
4use serde_json;
5use std;
6use std::collections::HashMap;
7use std::hash::{BuildHasher, Hash};
8
9/// Trait for validating glTF JSON data against the 2.0 specification.
10pub trait Validate {
11    /// Validates only the invariants required for the library to function safely.
12    fn validate_minimally<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
13    where
14        P: Fn() -> Path,
15        R: FnMut(&dyn Fn() -> Path, Error),
16    {
17        // nop
18    }
19
20    /// Validates the data against the glTF 2.0 specification.
21    ///
22    /// # Notes
23    ///
24    /// The caller must also call `validate_minimally()` for full validation.
25    fn validate_completely<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
26    where
27        P: Fn() -> Path,
28        R: FnMut(&dyn Fn() -> Path, Error),
29    {
30        // nop
31    }
32}
33
34/// Specifies what kind of error occured during validation.
35#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub enum Error {
37    /// An index was found to be out of bounds.
38    IndexOutOfBounds,
39
40    /// An invalid value was identified.
41    Invalid,
42
43    /// Some required data has been omitted.
44    Missing,
45}
46
47/// Specifies a type that has been pre-validated during deserialization or otherwise.
48#[derive(Debug, Eq, Hash, PartialEq)]
49pub enum Checked<T> {
50    /// The item is valid.
51    Valid(T),
52
53    /// The item is invalid.
54    Invalid,
55}
56
57impl<T> Checked<T> {
58    /// Converts from `Checked<T>` to `Checked<&T>`.
59    pub fn as_ref(&self) -> Checked<&T> {
60        match *self {
61            Checked::Valid(ref item) => Checked::Valid(item),
62            Checked::Invalid => Checked::Invalid,
63        }
64    }
65
66    /// Takes ownership of the contained item if it is `Valid`.
67    ///
68    /// # Panics
69    ///
70    /// Panics if called on an `Invalid` item.
71    pub fn unwrap(self) -> T {
72        match self {
73            Checked::Valid(item) => item,
74            Checked::Invalid => panic!("attempted to unwrap an invalid item"),
75        }
76    }
77
78    /// Tests if item is valid    
79    pub fn is_valid(&self) -> bool {
80        match *self {
81            Checked::Valid(..) => true,
82            Checked::Invalid => false,
83        }
84    }
85}
86
87impl<T: Serialize> Serialize for Checked<T> {
88    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
89    where
90        S: Serializer,
91    {
92        match *self {
93            Checked::Valid(ref item) => item.serialize(serializer),
94            Checked::Invalid => Err(ser::Error::custom("invalid item")),
95        }
96    }
97}
98
99impl<T: Clone> Clone for Checked<T> {
100    fn clone(&self) -> Self {
101        match *self {
102            Checked::Valid(ref item) => Checked::Valid(item.clone()),
103            Checked::Invalid => Checked::Invalid,
104        }
105    }
106}
107
108impl<T: Copy> Copy for Checked<T> {}
109
110impl<T: Default> Default for Checked<T> {
111    fn default() -> Self {
112        Checked::Valid(T::default())
113    }
114}
115
116impl<T> Validate for Checked<T> {
117    fn validate_minimally<P, R>(&self, _root: &Root, path: P, report: &mut R)
118    where
119        P: Fn() -> Path,
120        R: FnMut(&dyn Fn() -> Path, Error),
121    {
122        match *self {
123            Checked::Valid(_) => {}
124            Checked::Invalid => report(&path, Error::Invalid),
125        }
126    }
127}
128
129impl<K: Eq + Hash + ToString + Validate, V: Validate, H: BuildHasher> Validate for HashMap<K, V, H> {
130    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
131    where
132        P: Fn() -> Path,
133        R: FnMut(&dyn Fn() -> Path, Error),
134    {
135        for (key, value) in self.iter() {
136            key.validate_minimally(root, || path().key(&key.to_string()), report);
137            value.validate_minimally(root, || path().key(&key.to_string()), report);
138        }
139    }
140
141    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
142    where
143        P: Fn() -> Path,
144        R: FnMut(&dyn Fn() -> Path, Error),
145    {
146        for (key, value) in self.iter() {
147            key.validate_completely(root, || path().key(&key.to_string()), report);
148            value.validate_completely(root, || path().key(&key.to_string()), report);
149        }
150    }
151}
152
153impl<T: Validate> Validate for Option<T> {
154    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
155    where
156        P: Fn() -> Path,
157        R: FnMut(&dyn Fn() -> Path, Error),
158    {
159        if let Some(value) = self.as_ref() {
160            value.validate_minimally(root, path, report);
161        }
162    }
163
164    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
165    where
166        P: Fn() -> Path,
167        R: FnMut(&dyn Fn() -> Path, Error),
168    {
169        if let Some(value) = self.as_ref() {
170            value.validate_completely(root, path, report);
171        }
172    }
173}
174
175impl<T: Validate> Validate for Vec<T> {
176    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
177    where
178        P: Fn() -> Path,
179        R: FnMut(&dyn Fn() -> Path, Error),
180    {
181        for (index, value) in self.iter().enumerate() {
182            value.validate_minimally(root, || path().index(index), report);
183        }
184    }
185
186    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
187    where
188        P: Fn() -> Path,
189        R: FnMut(&dyn Fn() -> Path, Error),
190    {
191        for (index, value) in self.iter().enumerate() {
192            value.validate_completely(root, || path().index(index), report);
193        }
194    }
195}
196
197impl std::error::Error for Error {
198    fn description(&self) -> &str {
199        match *self {
200            Error::IndexOutOfBounds => "Index out of bounds",
201            Error::Invalid => "Invalid value",
202            Error::Missing => "Missing data",
203        }
204    }
205}
206
207impl std::fmt::Display for Error {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        use std::error::Error;
210        write!(f, "{}", self.description())
211    }
212}
213
214// These types are assumed to be always valid.
215impl Validate for bool {}
216impl Validate for u32 {}
217impl Validate for i32 {}
218impl Validate for f32 {}
219impl Validate for [f32; 3] {}
220impl Validate for [f32; 4] {}
221impl Validate for [f32; 16] {}
222impl Validate for () {}
223impl Validate for String {}
224impl Validate for serde_json::Value {}