gltf_v1_json/
validation.rs

1use std::collections::BTreeMap;
2use std::fmt::Display;
3
4use indexmap::IndexMap;
5use serde::ser;
6use serde::{Serialize, Serializer};
7
8use crate::{Path, Root};
9
10/// Trait for validating glTF JSON data so that the library can function without panicking.
11pub trait Validate {
12    /// Validates the invariants required for the library to function safely.
13    fn validate<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
14    where
15        P: Fn() -> Path,
16        R: FnMut(&dyn Fn() -> Path, Error),
17    {
18        // nop
19    }
20}
21
22/// Specifies what kind of error occured during validation.
23#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
24pub enum Error {
25    /// An index was found to be out of bounds.
26    IndexNotFound,
27
28    /// An invalid value was identified.
29    Invalid,
30
31    /// Some required data has been omitted.
32    Missing,
33
34    /// A memory size or offset exceeds the system limits.
35    Oversize,
36
37    /// One of more required extensions is not supported by this crate version.
38    Unsupported,
39}
40
41/// Specifies a type that has been pre-validated during deserialization or otherwise.
42#[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
43pub enum Checked<T> {
44    /// The item is valid.
45    Valid(T),
46
47    /// The item is invalid.
48    Invalid,
49}
50
51impl<T> Display for Checked<T>
52where
53    T: Display,
54{
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        match self {
57            Checked::Valid(valid) => f.write_fmt(format_args!("Valid: {}", valid)),
58            Checked::Invalid => f.write_str("Invalid"),
59        }
60    }
61}
62
63impl<T> Checked<T> {
64    /// Converts from `Checked<T>` to `Checked<&T>`.
65    pub fn as_ref(&self) -> Checked<&T> {
66        match *self {
67            Checked::Valid(ref item) => Checked::Valid(item),
68            Checked::Invalid => Checked::Invalid,
69        }
70    }
71
72    /// Takes ownership of the contained item if it is `Valid`.
73    ///
74    /// # Panics
75    ///
76    /// Panics if called on an `Invalid` item.
77    pub fn unwrap(self) -> T {
78        match self {
79            Checked::Valid(item) => item,
80            Checked::Invalid => panic!("attempted to unwrap an invalid item"),
81        }
82    }
83}
84
85impl<T: Serialize> Serialize for Checked<T> {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        match *self {
91            Checked::Valid(ref item) => item.serialize(serializer),
92            Checked::Invalid => Err(ser::Error::custom("invalid item")),
93        }
94    }
95}
96
97impl<T: Clone> Clone for Checked<T> {
98    fn clone(&self) -> Self {
99        match *self {
100            Checked::Valid(ref item) => Checked::Valid(item.clone()),
101            Checked::Invalid => Checked::Invalid,
102        }
103    }
104}
105
106impl<T: Copy> Copy for Checked<T> {}
107
108impl<T: Default> Default for Checked<T> {
109    fn default() -> Self {
110        Checked::Valid(T::default())
111    }
112}
113
114impl<T> Validate for Checked<T> {
115    fn validate<P, R>(&self, _root: &Root, path: P, report: &mut R)
116    where
117        P: Fn() -> Path,
118        R: FnMut(&dyn Fn() -> Path, Error),
119    {
120        match *self {
121            Checked::Valid(_) => {}
122            Checked::Invalid => report(&path, Error::Invalid),
123        }
124    }
125}
126
127/// Validates the suitability of 64-bit byte offsets/sizes on 32-bit systems.
128#[derive(
129    Clone,
130    Copy,
131    Debug,
132    Default,
133    Eq,
134    Hash,
135    PartialEq,
136    serde_derive::Deserialize,
137    serde_derive::Serialize,
138)]
139pub struct USize64(pub u64);
140
141impl From<u64> for USize64 {
142    fn from(value: u64) -> Self {
143        Self(value)
144    }
145}
146
147impl From<usize> for USize64 {
148    fn from(value: usize) -> Self {
149        Self(value as u64)
150    }
151}
152
153impl Validate for USize64 {
154    fn validate<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 usize::try_from(self.0).is_err() {
160            report(&path, Error::Oversize);
161        }
162    }
163}
164
165impl<K: ToString + Validate, V: Validate> Validate for IndexMap<K, V> {
166    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
167    where
168        P: Fn() -> Path,
169        R: FnMut(&dyn Fn() -> Path, Error),
170    {
171        for (key, value) in self.iter() {
172            key.validate(root, || path().key(&key.to_string()), report);
173            value.validate(root, || path().key(&key.to_string()), report);
174        }
175    }
176}
177
178impl<K: ToString + Validate, V: Validate> Validate for BTreeMap<K, V> {
179    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
180    where
181        P: Fn() -> Path,
182        R: FnMut(&dyn Fn() -> Path, Error),
183    {
184        for (key, value) in self.iter() {
185            key.validate(root, || path().key(&key.to_string()), report);
186            value.validate(root, || path().key(&key.to_string()), report);
187        }
188    }
189}
190
191impl Validate for serde_json::Map<String, serde_json::Value> {
192    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
193    where
194        P: Fn() -> Path,
195        R: FnMut(&dyn Fn() -> Path, Error),
196    {
197        for (key, value) in self.iter() {
198            key.validate(root, || path().key(&key.to_string()), report);
199            value.validate(root, || path().key(&key.to_string()), report);
200        }
201    }
202}
203
204impl<T: Validate> Validate for Option<T> {
205    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
206    where
207        P: Fn() -> Path,
208        R: FnMut(&dyn Fn() -> Path, Error),
209    {
210        if let Some(value) = self.as_ref() {
211            value.validate(root, path, report);
212        }
213    }
214}
215
216impl<T: Validate> Validate for Vec<T> {
217    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
218    where
219        P: Fn() -> Path,
220        R: FnMut(&dyn Fn() -> Path, Error),
221    {
222        for (index, value) in self.iter().enumerate() {
223            value.validate(root, || path().index(index), report);
224        }
225    }
226}
227
228// impl Validate for std::boxed::Box<serde_json::value::RawValue> {
229//     fn validate<P, R>(&self, _: &GLTF, _: P, _: &mut R)
230//     where
231//         P: Fn() -> Path,
232//         R: FnMut(&dyn Fn() -> Path, Error),
233//     {
234//         // nop
235//     }
236// }
237impl std::error::Error for Error {}
238
239impl std::fmt::Display for Error {
240    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
241        write!(
242            f,
243            "{}",
244            match *self {
245                Error::IndexNotFound => "Index not found",
246                Error::Invalid => "Invalid value",
247                Error::Missing => "Missing data",
248                Error::Oversize => "Size exceeds system limits",
249                Error::Unsupported => "Unsupported extension",
250            }
251        )
252    }
253}
254
255impl Validate for bool {}
256impl Validate for u32 {}
257impl Validate for i32 {}
258impl Validate for f32 {}
259impl Validate for () {}
260impl Validate for String {}
261impl Validate for serde_json::Value {}
262impl<T: Validate, const N: usize> Validate for [T; N] {}