Skip to content

2024 / Version 2.0 Plan #409

@alteous

Description

@alteous

2024 / Version 2.0 Plan

Happy new year all!

As discussed in issue #385, the next released version is expected to be 2.0 and this version is intended to mark the start of semantic versioning for all the crates in this repository rather than just the top-level gltf crate.

There are a few critical requirements that would need to be met for this to be reasonably achievable.

Critical requirements

Merging of validation errors with parsing errors to simplify the JSON data structures

This pertains to the Checked type:

pub enum Checked<T> {
   Valid(T),
   Invalid,
}

The original idea behind this was to separate validation errors (i.e., "the data does not make sense") from parsing errors (e.g., "an object missing its closing brace"). Where validation errors occur a JSON path to the offending datum can be reported. This is achieved using a custom deserialiser. For example, consider the camera::Type enumeration:

/// Specifies the camera type.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
    /// A perspective projection.
    Perspective = 1,

    /// An orthographic projection.
    Orthographic,
}

impl<'de> de::Deserialize<'de> for Checked<Type> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Type>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_CAMERA_TYPES)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                use self::Type::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "perspective" => Valid(Perspective),
                    "orthographic" => Valid(Orthographic),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

The use of the Checked type allows parsing to complete even if the data is malformed; however, pragmatically speaking, if the data is malformed then what use is it to continue parsing other than to provide the JSON path? Discontinuing this practice would allow the code to be generated trivially by serde_derive instead. For exporters, it would also simplify populating the JSON data structures.

/// Specifies the camera type.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(serde_derive::Deserialize, serde_derive::Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Type {
    /// A perspective projection.
    Perspective = 1,

    /// An orthographic projection.
    Orthographic,
}

Unification of the gltf and gltf-json crates

Historically, this split served two requirements: (1) to reduce build times and (2) to allow users to avoid using the wrapper. The first point might still be an issue. The second point could be achieved by generating the wrapper (see "Generation of wrapper" below) using a feature flag. Adding a feature flag to each wrapper type manually would be a pain otherwise. We could forego the second requirement for the time being.

Automated semantic versioning checks

This will make a stronger semantic versioning guarantee, reducing the chance of human error. See #385 (comment) for details.

Important but not critical requirements

Generation of wrapper

This has been discussed in other issues such as #198 and #234. A significant time period has elapsed since these issues were written and now I would feel more comfortable implementing a procedural macro on our existing data structures than implementating any other method of code generation. I have begun prototyping gltf-derive 2.0 to provide such macros.

There is a decision to be made regarding the structure of the generated crate. The existing structure is designed to keep type names terse and to avoid repeated prefixes. For example: Buffer, BufferView, and BufferTarget are grouped together as buffer::Buffer, buffer::View, and buffer::Target. All of the JSON data structures (i.e., those defined in the gltf-json crate) are re-exported under the json module and the crate structure matches that of the wrapper. For example: json::buffer::View corresponds to buffer::View. This structure could be difficult to replicate with a procedural macro. It is likely some compromise would have to be made such as buffer::View corresponding to either buffer::ViewJson or buffer::ViewReader.

"Trusted" imports

The validation step is reportedly slow (see #402). The purpose of these checks is to ensure ahead of time that the wrapper crate will not crash due to malformed data such as out of range indices. This is reasonable for arbitrary incoming data; however, for applications such as games with static assets, this is an unnecessary overhead. For these scenarios, I'd like to introduce "trusted" variants of the import functions that skip the validation step.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions