From 5dd270adb7568ae21cf613fe5f2e7ad7e95895d8 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Sun, 10 Nov 2024 16:19:34 +1100 Subject: [PATCH] Refactor `ArrayMetadata{V3,V2}` constructors --- zarrs/src/array/array_builder.rs | 26 ++++++----- zarrs_metadata/CHANGELOG.md | 14 ++++-- zarrs_metadata/src/v2/array.rs | 63 ++++++++++++++++++++++++++ zarrs_metadata/src/v2_to_v3.rs | 20 +++------ zarrs_metadata/src/v3/array.rs | 76 +++++++++++++++++++++++++++----- 5 files changed, 158 insertions(+), 41 deletions(-) diff --git a/zarrs/src/array/array_builder.rs b/zarrs/src/array/array_builder.rs index 29bca27e..48a58b82 100644 --- a/zarrs/src/array/array_builder.rs +++ b/zarrs/src/array/array_builder.rs @@ -314,18 +314,20 @@ impl ArrayBuilder { self.bytes_to_bytes_codecs.clone(), ); - let array_metadata = ArrayMetadata::V3(ArrayMetadataV3::new( - self.shape.clone(), - self.data_type.metadata(), - self.chunk_grid.create_metadata(), - self.chunk_key_encoding.create_metadata(), - self.data_type.metadata_fill_value(&self.fill_value), - codec_chain.create_metadatas(), - self.attributes.clone(), - self.storage_transformers.create_metadatas(), - self.dimension_names.clone(), - self.additional_fields.clone(), - )); + let array_metadata = ArrayMetadata::V3( + ArrayMetadataV3::new( + self.shape.clone(), + self.chunk_grid.create_metadata(), + self.data_type.metadata(), + self.data_type.metadata_fill_value(&self.fill_value), + codec_chain.create_metadatas(), + ) + .with_attributes(self.attributes.clone()) + .with_additional_fields(self.additional_fields.clone()) + .with_chunk_key_encoding(self.chunk_key_encoding.create_metadata()) + .with_dimension_names(self.dimension_names.clone()) + .with_storage_transformers(self.storage_transformers.create_metadatas()), + ); Ok(Array { storage, diff --git a/zarrs_metadata/CHANGELOG.md b/zarrs_metadata/CHANGELOG.md index 4fcf69f3..cb616c75 100644 --- a/zarrs_metadata/CHANGELOG.md +++ b/zarrs_metadata/CHANGELOG.md @@ -7,13 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Add `GroupMetadataV2` constructors +- Add `ArrayMetadataV2` constructors + ### Changed - **Breaking**: Mark `GroupMetadataV3` and `ArrayMetadataV3` as non-exhaustive - **Breaking**: Bump MSRV to 1.77 (21 March, 2024) -- Refactor `GroupMetadata{V3,V2}` constructors - - **Breaking**: `GroupMetadataV3::new()` is now parameter free - - Add `GroupMetadataV2::new()` - - Add `GroupMetadata{V3,V2}::with_{attributes,additional_fields}()` +- Refactor `GroupMetadataV3` constructors + - **Breaking**: `GroupMetadataV3::new()` is now parameter free in favor of `with_` methods + - Add `GroupMetadataV3::with_{attributes,additional_fields}()` +- Refactor `ArrayMetadataV3` constructors + - **Breaking**: `ArrayMetadataV3::new()` takes fewer parameters in favor of `with_` methods + - Add `ArrayMetadataV3::with_{attributes,additional_fields,chunk_key_encoding,dimension_names,storage_transformers}` ## [0.1.0] - 2024-09-02 diff --git a/zarrs_metadata/src/v2/array.rs b/zarrs_metadata/src/v2/array.rs index 1ff4e5e0..4b57f73b 100644 --- a/zarrs_metadata/src/v2/array.rs +++ b/zarrs_metadata/src/v2/array.rs @@ -84,6 +84,69 @@ pub struct ArrayMetadataV2 { pub additional_fields: AdditionalFields, } +impl ArrayMetadataV2 { + /// Create Zarr V2 array metadata. + /// + /// Defaults to: + /// - C order, + /// - empty attributes, and + /// - no additional fields. + #[must_use] + pub fn new( + shape: ArrayShape, + chunks: ChunkShape, + dtype: DataTypeMetadataV2, + fill_value: FillValueMetadataV2, + compressor: Option, + filters: Option>, + ) -> Self { + Self { + zarr_format: monostate::MustBe!(2u64), + shape, + chunks, + dtype, + compressor, + fill_value, + order: ArrayMetadataV2Order::C, + filters, + dimension_separator: ChunkKeySeparator::Dot, + attributes: serde_json::Map::default(), + additional_fields: AdditionalFields::default(), + } + } + + /// Set the dimension separator. + #[must_use] + pub fn with_dimension_separator(mut self, dimension_separator: ChunkKeySeparator) -> Self { + self.dimension_separator = dimension_separator; + self + } + + /// Set the order. + #[must_use] + pub fn with_order(mut self, order: ArrayMetadataV2Order) -> Self { + self.order = order; + self + } + + /// Set the user attributes. + #[must_use] + pub fn with_attributes( + mut self, + attributes: serde_json::Map, + ) -> Self { + self.attributes = attributes; + self + } + + /// Set the additional fields. + #[must_use] + pub fn with_additional_fields(mut self, additional_fields: AdditionalFields) -> Self { + self.additional_fields = additional_fields; + self + } +} + const fn chunk_key_separator_default_zarr_v2() -> ChunkKeySeparator { ChunkKeySeparator::Dot } diff --git a/zarrs_metadata/src/v2_to_v3.rs b/zarrs_metadata/src/v2_to_v3.rs index 12448017..965ae28a 100644 --- a/zarrs_metadata/src/v2_to_v3.rs +++ b/zarrs_metadata/src/v2_to_v3.rs @@ -23,7 +23,7 @@ use crate::{ }, fill_value::{FillValueFloat, FillValueFloatStringNonFinite, FillValueMetadataV3}, }, - AdditionalFields, ArrayMetadataV3, GroupMetadataV3, MetadataV3, + ArrayMetadataV3, GroupMetadataV3, MetadataV3, }, }; @@ -231,18 +231,12 @@ pub fn array_metadata_v2_to_v3( let attributes = array_metadata_v2.attributes.clone(); - Ok(ArrayMetadataV3::new( - shape, - data_type, - chunk_grid, - chunk_key_encoding, - fill_value, - codecs, - attributes, - vec![], - None, - AdditionalFields::default(), - )) + Ok( + ArrayMetadataV3::new(shape, chunk_grid, data_type, fill_value, codecs) + .with_attributes(attributes) + .with_additional_fields(array_metadata_v2.additional_fields.clone()) + .with_chunk_key_encoding(chunk_key_encoding), + ) } /// An unsupported Zarr V2 data type error. diff --git a/zarrs_metadata/src/v3/array.rs b/zarrs_metadata/src/v3/array.rs index 1bca250d..4df7a127 100644 --- a/zarrs_metadata/src/v3/array.rs +++ b/zarrs_metadata/src/v3/array.rs @@ -1,3 +1,4 @@ +use chunk_key_encoding::default::DefaultChunkKeyEncodingConfiguration; use data_type::DataTypeMetadataV3; use derive_more::Display; use fill_value::FillValueMetadataV3; @@ -98,6 +99,7 @@ pub mod nan_representations; /// } /// ``` #[non_exhaustive] +#[allow(clippy::unsafe_derive_deserialize)] #[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Display)] #[display("{}", serde_json::to_string(self).unwrap_or_default())] pub struct ArrayMetadataV3 { @@ -154,21 +156,33 @@ pub struct ArrayMetadataV3 { } impl ArrayMetadataV3 { - /// Create a new array metadata. - #[allow(clippy::too_many_arguments)] + /// Create new Zarr V3 array metadata. + /// + /// Defaults to: + /// - `default` chunk key encoding with the '/' separator, + /// - empty attributes, + /// - no dimension names, + /// - no storage transformers, and + /// - no additional fields. #[must_use] pub fn new( shape: ArrayShape, - data_type: DataTypeMetadataV3, chunk_grid: MetadataV3, - chunk_key_encoding: MetadataV3, + data_type: DataTypeMetadataV3, fill_value: FillValueMetadataV3, codecs: Vec, - attributes: serde_json::Map, - storage_transformers: Vec, - dimension_names: Option>, - additional_fields: AdditionalFields, ) -> Self { + let chunk_key_encoding = unsafe { + // SAFETY: The default chunk key encoding configuration is valid JSON. + MetadataV3::new_with_serializable_configuration( + crate::v3::array::chunk_key_encoding::default::IDENTIFIER, + &DefaultChunkKeyEncodingConfiguration { + separator: crate::ChunkKeySeparator::Slash, + }, + ) + .unwrap_unchecked() + }; + Self { zarr_format: monostate::MustBe!(3u64), node_type: monostate::MustBe!("array"), @@ -178,10 +192,48 @@ impl ArrayMetadataV3 { chunk_key_encoding, fill_value, codecs, - attributes, - storage_transformers, - dimension_names, - additional_fields, + attributes: serde_json::Map::default(), + storage_transformers: Vec::default(), + dimension_names: None, + additional_fields: AdditionalFields::default(), } } + + /// Set the user attributes. + #[must_use] + pub fn with_attributes( + mut self, + attributes: serde_json::Map, + ) -> Self { + self.attributes = attributes; + self + } + + /// Set the additional fields. + #[must_use] + pub fn with_additional_fields(mut self, additional_fields: AdditionalFields) -> Self { + self.additional_fields = additional_fields; + self + } + + /// Set the chunk key encoding. + #[must_use] + pub fn with_chunk_key_encoding(mut self, chunk_key_encoding: MetadataV3) -> Self { + self.chunk_key_encoding = chunk_key_encoding; + self + } + + /// Set the dimension names. + #[must_use] + pub fn with_dimension_names(mut self, dimension_names: Option>) -> Self { + self.dimension_names = dimension_names; + self + } + + /// Set the storage transformers. + #[must_use] + pub fn with_storage_transformers(mut self, storage_transformers: Vec) -> Self { + self.storage_transformers = storage_transformers; + self + } }