Skip to content

Commit

Permalink
Use new validate_hook everywhere except animation
Browse files Browse the repository at this point in the history
  • Loading branch information
alteous committed Apr 22, 2024
1 parent 01b0405 commit 33ce6a1
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 158 deletions.
44 changes: 12 additions & 32 deletions gltf-json/src/accessor.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::validation::{Checked, Error, USize64, Validate};
use crate::validation::{Checked, Error, USize64};
use crate::{buffer, extensions, Extras, Index, Path, Root};
use gltf_derive::Validate;
use serde::{de, ser};
Expand Down Expand Up @@ -169,7 +169,8 @@ pub mod sparse {
}

/// A typed view into a buffer view.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "accessor_validate_hook")]
pub struct Accessor {
/// The parent buffer view this accessor reads from.
///
Expand Down Expand Up @@ -231,36 +232,15 @@ pub struct Accessor {
pub sparse: Option<sparse::Sparse>,
}

impl Validate for Accessor {
fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if self.sparse.is_none() && self.buffer_view.is_none() {
// If sparse is missing, then bufferView must be present. Report that bufferView is
// missing since it is the more common one to require.
report(&|| path().field("bufferView"), Error::Missing);
}

self.buffer_view
.validate(root, || path().field("bufferView"), report);
self.byte_offset
.validate(root, || path().field("byteOffset"), report);
self.count.validate(root, || path().field("count"), report);
self.component_type
.validate(root, || path().field("componentType"), report);
self.extensions
.validate(root, || path().field("extensions"), report);
self.extras
.validate(root, || path().field("extras"), report);
self.type_.validate(root, || path().field("type"), report);
self.min.validate(root, || path().field("min"), report);
self.max.validate(root, || path().field("max"), report);
self.normalized
.validate(root, || path().field("normalized"), report);
self.sparse
.validate(root, || path().field("sparse"), report);
fn accessor_validate_hook<P, R>(accessor: &Accessor, _root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if accessor.sparse.is_none() && accessor.buffer_view.is_none() {
// If sparse is missing, then bufferView must be present. Report that bufferView is
// missing since it is the more common one to require.
report(&|| path().field("bufferView"), Error::Missing);
}
}

Expand Down
37 changes: 13 additions & 24 deletions gltf-json/src/camera.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::validation::{Checked, Error, Validate};
use crate::validation::{Checked, Error};
use crate::{extensions, Extras, Path, Root};
use gltf_derive::Validate;
use serde::{de, ser};
Expand All @@ -22,7 +22,8 @@ pub enum Type {
///
/// A node can reference a camera to apply a transform to place the camera in the
/// scene.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "camera_validate_hook")]
pub struct Camera {
/// Optional user-defined name for this object.
#[cfg(feature = "names")]
Expand Down Expand Up @@ -54,6 +55,16 @@ pub struct Camera {
pub extras: Extras,
}

fn camera_validate_hook<P, R>(camera: &Camera, _root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if camera.orthographic.is_none() && camera.perspective.is_none() {
report(&path, Error::Missing);
}
}

/// Values for an orthographic camera.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Orthographic {
Expand Down Expand Up @@ -109,28 +120,6 @@ pub struct Perspective {
pub extras: Extras,
}

impl Validate for Camera {
fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if self.orthographic.is_none() && self.perspective.is_none() {
report(&path, Error::Missing);
}

self.orthographic
.validate(root, || path().field("orthographic"), report);
self.perspective
.validate(root, || path().field("perspective"), report);
self.type_.validate(root, || path().field("type"), report);
self.extensions
.validate(root, || path().field("extensions"), report);
self.extras
.validate(root, || path().field("extras"), report);
}
}

impl<'de> de::Deserialize<'de> for Checked<Type> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
4 changes: 2 additions & 2 deletions gltf-json/src/extensions/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use serde_derive::{Deserialize, Serialize};
use serde_json::{Map, Value};

/// A keyframe animation.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Animation {
#[cfg(feature = "extensions")]
#[serde(default, flatten)]
pub others: Map<String, Value>,
}

/// Targets an animation's sampler at a node's property.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
pub struct Channel {}

/// The index of the node and TRS property that an animation channel targets.
Expand Down
29 changes: 11 additions & 18 deletions gltf-json/src/extensions/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct Node {

#[cfg(feature = "KHR_lights_punctual")]
pub mod khr_lights_punctual {
use crate::validation::{Checked, Error, Validate};
use crate::validation::{Checked, Error};
use crate::{Extras, Index, Path, Root};
use gltf_derive::Validate;
use serde::{de, ser};
Expand Down Expand Up @@ -76,7 +76,8 @@ pub mod khr_lights_punctual {
Spot,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "light_validate_hook")]
pub struct Light {
/// Color of the light source.
#[serde(default = "color_default")]
Expand Down Expand Up @@ -116,23 +117,15 @@ pub mod khr_lights_punctual {
pub type_: Checked<Type>,
}

impl Validate for Light {
fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if let Checked::Valid(ty) = self.type_.as_ref() {
if *ty == Type::Spot && self.spot.is_none() {
report(&|| path().field("spot"), Error::Missing);
}
fn light_validate_hook<P, R>(light: &Light, _root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
if let Checked::Valid(ty) = light.type_.as_ref() {
if *ty == Type::Spot && light.spot.is_none() {
report(&|| path().field("spot"), Error::Missing);
}

self.type_.validate(root, || path().field("type"), report);
self.extensions
.validate(root, || path().field("extensions"), report);
self.extras
.validate(root, || path().field("extras"), report);
}
}

Expand Down
73 changes: 29 additions & 44 deletions gltf-json/src/mesh.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::validation::{Checked, Error, Validate};
use crate::validation::{Checked, Error};
use crate::{accessor, extensions, material, Extras, Index};
use gltf_derive::Validate;
use serde::{de, ser};
Expand Down Expand Up @@ -97,7 +97,8 @@ pub struct Mesh {
}

/// Geometry to be rendered with the given material.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "primitive_validate_hook")]
pub struct Primitive {
/// Maps attribute semantic names to the `Accessor`s containing the
/// corresponding attribute data.
Expand Down Expand Up @@ -136,54 +137,38 @@ fn is_primitive_mode_default(mode: &Checked<Mode>) -> bool {
*mode == Checked::Valid(Mode::Triangles)
}

impl Validate for Primitive {
fn validate<P, R>(&self, root: &crate::Root, path: P, report: &mut R)
where
P: Fn() -> crate::Path,
R: FnMut(&dyn Fn() -> crate::Path, crate::validation::Error),
fn primitive_validate_hook<P, R>(primitive: &Primitive, root: &crate::Root, path: P, report: &mut R)
where
P: Fn() -> crate::Path,
R: FnMut(&dyn Fn() -> crate::Path, crate::validation::Error),
{
let position_path = &|| path().field("attributes").key("POSITION");
if let Some(pos_accessor_index) = primitive
.attributes
.get(&Checked::Valid(Semantic::Positions))
{
// Generated part
self.attributes
.validate(root, || path().field("attributes"), report);
self.extensions
.validate(root, || path().field("extensions"), report);
self.extras
.validate(root, || path().field("extras"), report);
self.indices
.validate(root, || path().field("indices"), report);
self.material
.validate(root, || path().field("material"), report);
self.mode.validate(root, || path().field("mode"), report);
self.targets
.validate(root, || path().field("targets"), report);

// Custom part
let position_path = &|| path().field("attributes").key("POSITION");
if let Some(pos_accessor_index) = self.attributes.get(&Checked::Valid(Semantic::Positions))
{
// spec: POSITION accessor **must** have `min` and `max` properties defined.
let pos_accessor = &root.accessors[pos_accessor_index.value()];

let min_path = &|| position_path().field("min");
if let Some(ref min) = pos_accessor.min {
if from_value::<[f32; 3]>(min.clone()).is_err() {
report(min_path, Error::Invalid);
}
} else {
report(min_path, Error::Missing);
// spec: POSITION accessor **must** have `min` and `max` properties defined.
let pos_accessor = &root.accessors[pos_accessor_index.value()];

let min_path = &|| position_path().field("min");
if let Some(ref min) = pos_accessor.min {
if from_value::<[f32; 3]>(min.clone()).is_err() {
report(min_path, Error::Invalid);
}
} else {
report(min_path, Error::Missing);
}

let max_path = &|| position_path().field("max");
if let Some(ref max) = pos_accessor.max {
if from_value::<[f32; 3]>(max.clone()).is_err() {
report(max_path, Error::Invalid);
}
} else {
report(max_path, Error::Missing);
let max_path = &|| position_path().field("max");
if let Some(ref max) = pos_accessor.max {
if from_value::<[f32; 3]>(max.clone()).is_err() {
report(max_path, Error::Invalid);
}
} else {
report(position_path, Error::Missing);
report(max_path, Error::Missing);
}
} else {
report(position_path, Error::Missing);
}
}

Expand Down
40 changes: 20 additions & 20 deletions gltf-json/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,6 @@ impl<T> Index<T> {
}
}

fn root_validate_hook<P, R>(root: &Root, _also_root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, crate::validation::Error),
{
for (i, ext) in root.extensions_required.iter().enumerate() {
if !crate::extensions::ENABLED_EXTENSIONS.contains(&ext.as_str()) {
report(
&|| {
path()
.field("extensionsRequired")
.index(i)
.value_str(ext.as_str())
},
crate::validation::Error::Unsupported,
);
}
}
}

/// The root object of a glTF 2.0 asset.
#[derive(Clone, Debug, Default, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "root_validate_hook")]
Expand Down Expand Up @@ -172,6 +152,26 @@ pub struct Root {
pub textures: Vec<Texture>,
}

fn root_validate_hook<P, R>(root: &Root, _also_root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, crate::validation::Error),
{
for (i, ext) in root.extensions_required.iter().enumerate() {
if !crate::extensions::ENABLED_EXTENSIONS.contains(&ext.as_str()) {
report(
&|| {
path()
.field("extensionsRequired")
.index(i)
.value_str(ext.as_str())
},
crate::validation::Error::Unsupported,
);
}
}
}

impl Root {
/// Returns a single item from the root object.
pub fn get<T>(&self, index: Index<T>) -> Option<&T>
Expand Down
30 changes: 12 additions & 18 deletions gltf-json/src/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ pub struct Sampler {
}

/// A texture and its sampler.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
#[gltf(validate_hook = "texture_validate_hook")]
pub struct Texture {
/// Optional user-defined name for this object.
#[cfg(feature = "names")]
Expand All @@ -193,23 +194,16 @@ pub struct Texture {
pub extras: Extras,
}

impl Validate for Texture {
fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
self.sampler
.validate(root, || path().field("sampler"), report);

{
let source_path = || path().field("source");
if let Some(index) = self.source.as_ref() {
index.validate(root, source_path, report);
} else {
report(&source_path, Error::Missing);
}
}
fn texture_validate_hook<P, R>(texture: &Texture, root: &Root, path: P, report: &mut R)
where
P: Fn() -> Path,
R: FnMut(&dyn Fn() -> Path, Error),
{
let source_path = || path().field("source");
if let Some(index) = texture.source.as_ref() {
index.validate(root, source_path, report);
} else {
report(&source_path, Error::Missing);
}
}

Expand Down

0 comments on commit 33ce6a1

Please sign in to comment.