use serde::{ser, Serialize, Serializer}; use std::collections::BTreeMap; use std::hash::Hash; use crate::{Path, Root}; /// Trait for validating glTF JSON data so that the library can function without panicking. pub trait Validate { /// Validates the invariants required for the library to function safely. fn validate(&self, _root: &Root, _path: P, _report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { // nop } } /// Specifies what kind of error occured during validation. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Error { /// An index was found to be out of bounds. IndexOutOfBounds, /// An invalid value was identified. Invalid, /// Some required data has been omitted. Missing, /// A memory size or offset exceeds the system limits. Oversize, /// One of more required extensions is not supported by this crate version. Unsupported, } /// Specifies a type that has been pre-validated during deserialization or otherwise. #[derive(Debug, Eq, Hash, PartialEq, Ord, PartialOrd)] pub enum Checked { /// The item is valid. Valid(T), /// The item is invalid. Invalid, } impl Checked { /// Converts from `Checked` to `Checked<&T>`. pub fn as_ref(&self) -> Checked<&T> { match *self { Checked::Valid(ref item) => Checked::Valid(item), Checked::Invalid => Checked::Invalid, } } /// Takes ownership of the contained item if it is `Valid`. /// /// # Panics /// /// Panics if called on an `Invalid` item. pub fn unwrap(self) -> T { match self { Checked::Valid(item) => item, Checked::Invalid => panic!("attempted to unwrap an invalid item"), } } } impl Serialize for Checked { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { Checked::Valid(ref item) => item.serialize(serializer), Checked::Invalid => Err(ser::Error::custom("invalid item")), } } } impl Clone for Checked { fn clone(&self) -> Self { match *self { Checked::Valid(ref item) => Checked::Valid(item.clone()), Checked::Invalid => Checked::Invalid, } } } impl Copy for Checked {} impl Default for Checked { fn default() -> Self { Checked::Valid(T::default()) } } impl Validate for Checked { fn validate(&self, _root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { match *self { Checked::Valid(_) => {} Checked::Invalid => report(&path, Error::Invalid), } } } /// Validates the suitability of 64-bit byte offsets/sizes on 32-bit systems. #[derive( Clone, Copy, Debug, Default, Eq, Hash, PartialEq, serde_derive::Deserialize, serde_derive::Serialize, )] pub struct USize64(pub u64); impl From for USize64 { fn from(value: u64) -> Self { Self(value) } } impl From for USize64 { fn from(value: usize) -> Self { Self(value as u64) } } impl Validate for USize64 { fn validate(&self, _root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { if usize::try_from(self.0).is_err() { report(&path, Error::Oversize); } } } impl Validate for BTreeMap { fn validate(&self, root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { for (key, value) in self.iter() { key.validate(root, || path().key(&key.to_string()), report); value.validate(root, || path().key(&key.to_string()), report); } } } impl Validate for serde_json::Map { fn validate(&self, root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { for (key, value) in self.iter() { key.validate(root, || path().key(&key.to_string()), report); value.validate(root, || path().key(&key.to_string()), report); } } } impl Validate for Option { fn validate(&self, root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { if let Some(value) = self.as_ref() { value.validate(root, path, report); } } } impl Validate for Vec { fn validate(&self, root: &Root, path: P, report: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { for (index, value) in self.iter().enumerate() { value.validate(root, || path().index(index), report); } } } impl Validate for std::boxed::Box { fn validate(&self, _: &Root, _: P, _: &mut R) where P: Fn() -> Path, R: FnMut(&dyn Fn() -> Path, Error), { // nop } } impl std::error::Error for Error {} impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "{}", match *self { Error::IndexOutOfBounds => "Index out of bounds", Error::Invalid => "Invalid value", Error::Missing => "Missing data", Error::Oversize => "Size exceeds system limits", Error::Unsupported => "Unsupported extension", } ) } } // These types are assumed to be always valid. impl Validate for bool {} impl Validate for u32 {} impl Validate for i32 {} impl Validate for f32 {} impl Validate for [f32; 3] {} impl Validate for [f32; 4] {} impl Validate for [f32; 16] {} impl Validate for () {} impl Validate for String {} impl Validate for serde_json::Value {}