Skip to content

Commit

Permalink
feat(metadata): add support for metadata with must_understand
Browse files Browse the repository at this point in the history
  • Loading branch information
LDeakin committed Feb 21, 2025
1 parent e35bc65 commit 7071a66
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 6 deletions.
4 changes: 4 additions & 0 deletions zarrs_metadata/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Add support for a `must_understand` field to `MetadataV3` (ZEP0009)
- Extensions can now be parsed in more than just the additional fields of array/group metadata (e.g. codecs)

## [0.3.5] - 2025-02-18

### Fixed
Expand Down
92 changes: 86 additions & 6 deletions zarrs_metadata/src/v3/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,47 @@ use thiserror::Error;

/// Metadata with a name and optional configuration.
///
/// Represents most fields in Zarr V3 array metadata (see [`ArrayMetadataV3`](crate::v3::ArrayMetadataV3)), which is structured as JSON with a name and optional configuration, or just a string representing the name.
/// Represents most fields in Zarr V3 array metadata (see [`ArrayMetadataV3`](crate::v3::ArrayMetadataV3)) which is either:
/// - a string name / identifier, or
/// - a JSON object with a required `name` field and optional `configuration` and `must_understand` fields.
///
/// Can be deserialised from a JSON string or name/configuration map.
/// For example:
/// `must_understand` is implicitly set to [`true`] if omitted.
/// See [ZEP0009](https://zarr.dev/zeps/draft/ZEP0009.html) for more information on this field and Zarr V3 extensions.
///
/// ### Example Metadata
/// ```json
/// "bytes"
/// ```
/// or
///
/// ```json
/// {
/// "name": "bytes",
/// }
/// ```
/// or
///
/// ```json
/// {
/// "name": "bytes",
/// "configuration": {
/// "endian": "little"
/// }
/// }
/// ```
///
/// ```json
/// {
/// "name": "bytes",
/// "configuration": {
/// "endian": "little"
/// },
/// "must_understand": False
/// }
/// ```
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct MetadataV3 {
name: String,
configuration: Option<MetadataConfiguration>,
must_understand: bool,
}

/// Configuration metadata.
Expand Down Expand Up @@ -67,9 +82,12 @@ impl serde::Serialize for MetadataV3 {
s.serialize_entry("name", &self.name)?;
s.end()
} else {
let mut s = s.serialize_map(Some(2))?;
let mut s = s.serialize_map(Some(if self.must_understand { 2 } else { 3 }))?;
s.serialize_entry("name", &self.name)?;
s.serialize_entry("configuration", configuration)?;
if !self.must_understand {
s.serialize_entry("must_understand", &false)?;

Check warning on line 89 in zarrs_metadata/src/v3/metadata.rs

View check run for this annotation

Codecov / codecov/patch

zarrs_metadata/src/v3/metadata.rs#L89

Added line #L89 was not covered by tests
}
s.end()
}
} else {
Expand All @@ -78,6 +96,10 @@ impl serde::Serialize for MetadataV3 {
}
}

fn default_must_understand() -> bool {
true
}

impl<'de> serde::Deserialize<'de> for MetadataV3 {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
#[derive(Deserialize)]
Expand All @@ -86,6 +108,8 @@ impl<'de> serde::Deserialize<'de> for MetadataV3 {
name: String,
#[serde(default)]
configuration: Option<MetadataConfiguration>,
#[serde(default = "default_must_understand")]
must_understand: bool,
}

#[derive(Deserialize)]
Expand All @@ -102,10 +126,12 @@ impl<'de> serde::Deserialize<'de> for MetadataV3 {
MetadataIntermediate::Name(name) => Ok(Self {
name,
configuration: None,
must_understand: true,
}),
MetadataIntermediate::NameConfiguration(metadata) => Ok(Self {
name: metadata.name,
configuration: metadata.configuration,
must_understand: metadata.must_understand,
}),
}
}
Expand All @@ -118,6 +144,7 @@ impl MetadataV3 {
Self {
name: name.into(),
configuration: None,
must_understand: true,
}
}

Expand All @@ -127,9 +154,17 @@ impl MetadataV3 {
Self {
name: name.into(),
configuration: Some(configuration),
must_understand: true,
}
}

/// Set the value of the `must_understand` field.
#[must_use]
pub fn with_must_understand(mut self, must_understand: bool) -> Self {
self.must_understand = must_understand;
self
}

Check warning on line 166 in zarrs_metadata/src/v3/metadata.rs

View check run for this annotation

Codecov / codecov/patch

zarrs_metadata/src/v3/metadata.rs#L163-L166

Added lines #L163 - L166 were not covered by tests

/// Convert a serializable configuration to [`MetadataV3`].
///
/// # Errors
Expand Down Expand Up @@ -173,6 +208,14 @@ impl MetadataV3 {
self.configuration.as_ref()
}

/// Return whether the metadata must be understood as indicated by the `must_understand` field of the metadata.
///
/// The `must_understand` field is implicitly `true` if omitted.
#[must_use]
pub fn must_understand(&self) -> bool {
self.must_understand
}

/// Returns true if the configuration is none or an empty map.
#[must_use]
pub fn configuration_is_none_or_empty(&self) -> bool {
Expand Down Expand Up @@ -346,3 +389,40 @@ where
/// Additional fields in array or group metadata.
// NOTE: It would be nice if this was just a serde_json::Map, but it only has implementations for `<String, Value>`.
pub type AdditionalFields = std::collections::BTreeMap<String, AdditionalField>;

#[cfg(test)]
mod tests {
use super::MetadataV3;

#[test]
fn metadata_must_understand_implicit() {
let metadata = r#"{
"name": "test"
}"#;
let metadata: MetadataV3 = serde_json::from_str(&metadata).unwrap();
assert!(metadata.name() == "test");
assert!(metadata.must_understand());
}

#[test]
fn metadata_must_understand_true() {
let metadata = r#"{
"name": "test",
"must_understand": true
}"#;
let metadata: MetadataV3 = serde_json::from_str(&metadata).unwrap();
assert!(metadata.name() == "test");
assert!(metadata.must_understand());
}

#[test]
fn metadata_must_understand_false() {
let metadata = r#"{
"name": "test",
"must_understand": false
}"#;
let metadata: MetadataV3 = serde_json::from_str(&metadata).unwrap();
assert!(metadata.name() == "test");
assert!(!metadata.must_understand());
}
}

0 comments on commit 7071a66

Please sign in to comment.