[go: up one dir, main page]

Documentation
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use serde_json;
use std;

use json::*;
use std::collections::HashMap;
use std::hash::Hash;

/// Trait for validating glTF JSON data against the 2.0 specification.
pub trait Validate {
    /// Validates only the invariants required for the library to function safely.
    fn validate_minimally<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        // nop
    }

    /// Validates the data against the glTF 2.0 specification.
    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        self.validate_minimally(root, path, report)
    }
}

/// Specifies what kind of error occured during validation.
#[derive(Clone, Debug)]
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,
}

/// Specifies a type that has been pre-validated during deserialization or otherwise.
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Checked<T> {
    /// The item is valid.
    Valid(T),

    /// The item is invalid.
    Invalid,
}

impl<T> Checked<T> {
    /// Converts from `Checked<T>` 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<T: Clone> Clone for Checked<T> {
    fn clone(&self) -> Self {
        match self {
            &Checked::Valid(ref item) => Checked::Valid(item.clone()),
            &Checked::Invalid => Checked::Invalid,
        }
    }
}

impl<T: Copy> Copy for Checked<T> {}

impl<T: Default> Default for Checked<T> {
    fn default() -> Self {
        Checked::Valid(T::default())
    }
}

impl<T> Validate for Checked<T> {
    fn validate_minimally<P, R>(&self, _root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        match self {
            &Checked::Valid(_) => {},
            &Checked::Invalid => report(&path, Error::Invalid),
        }
    }
}

impl<K: Eq + Hash + ToString + Validate, V: Validate> Validate for HashMap<K, V> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_minimally(root, || path().key(&key.to_string()), report);
            value.validate_minimally(root, || path().key(&key.to_string()), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_completely(root, || path().key(&key.to_string()), report);
            value.validate_completely(root, || path().key(&key.to_string()), report);
        }
    }
}

impl<T: Validate> Validate for Option<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_minimally(root, path, report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_completely(root, path, report);
        }
    }
}

impl<T: Validate> Validate for Vec<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_minimally(root, || path().index(index), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_completely(root, || path().index(index), report);
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::IndexOutOfBounds => "Index out of bounds",
            &Error::Invalid => "Invalid value",
            &Error::Missing => "Missing data",
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

// 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 {}