-
Notifications
You must be signed in to change notification settings - Fork 150
Description
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.