gltf_json/
validation.rs

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