1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use super::DataType;
7use crate::error::{IoError, Result};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ArrayMetadataV2 {
14 pub zarr_format: u32,
16 pub shape: Vec<u64>,
18 pub chunks: Vec<u64>,
20 pub dtype: String,
22 pub compressor: Option<CompressorV2>,
24 pub fill_value: serde_json::Value,
26 pub order: String,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub filters: Option<Vec<serde_json::Value>>,
31 #[serde(default = "default_dimension_separator")]
33 pub dimension_separator: String,
34}
35
36fn default_dimension_separator() -> String {
37 ".".to_string()
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct CompressorV2 {
43 pub id: String,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub level: Option<i32>,
48 #[serde(flatten)]
50 pub extra: HashMap<String, serde_json::Value>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct GroupMetadataV2 {
56 pub zarr_format: u32,
58}
59
60impl GroupMetadataV2 {
61 pub fn new() -> Self {
63 Self { zarr_format: 2 }
64 }
65}
66
67impl Default for GroupMetadataV2 {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct ConsolidatedMetadata {
76 pub zarr_format: u32,
78 pub metadata: HashMap<String, serde_json::Value>,
80}
81
82impl ConsolidatedMetadata {
83 pub fn new() -> Self {
85 Self {
86 zarr_format: 2,
87 metadata: HashMap::new(),
88 }
89 }
90
91 pub fn insert_array(&mut self, path: &str, meta: &ArrayMetadataV2) -> Result<()> {
93 let key = format!("{path}/.zarray");
94 let value = serde_json::to_value(meta).map_err(|e| {
95 IoError::SerializationError(format!("Failed to serialize array metadata: {e}"))
96 })?;
97 self.metadata.insert(key, value);
98 Ok(())
99 }
100
101 pub fn insert_group(&mut self, path: &str, meta: &GroupMetadataV2) -> Result<()> {
103 let key = format!("{path}/.zgroup");
104 let value = serde_json::to_value(meta).map_err(|e| {
105 IoError::SerializationError(format!("Failed to serialize group metadata: {e}"))
106 })?;
107 self.metadata.insert(key, value);
108 Ok(())
109 }
110
111 pub fn to_json(&self) -> Result<Vec<u8>> {
113 serde_json::to_vec_pretty(self).map_err(|e| {
114 IoError::SerializationError(format!("Failed to serialize consolidated metadata: {e}"))
115 })
116 }
117
118 pub fn from_json(data: &[u8]) -> Result<Self> {
120 serde_json::from_slice(data).map_err(|e| {
121 IoError::DeserializationError(format!(
122 "Failed to deserialize consolidated metadata: {e}"
123 ))
124 })
125 }
126}
127
128impl Default for ConsolidatedMetadata {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl ArrayMetadataV2 {
135 pub fn new(
137 shape: Vec<u64>,
138 chunks: Vec<u64>,
139 dtype: DataType,
140 compressor: Option<CompressorV2>,
141 fill_value: serde_json::Value,
142 ) -> Self {
143 Self {
144 zarr_format: 2,
145 shape,
146 chunks,
147 dtype: dtype.to_v2_dtype().to_string(),
148 compressor,
149 fill_value,
150 order: "C".to_string(),
151 filters: None,
152 dimension_separator: ".".to_string(),
153 }
154 }
155
156 pub fn to_json(&self) -> Result<Vec<u8>> {
158 serde_json::to_vec_pretty(self)
159 .map_err(|e| IoError::SerializationError(format!("Failed to serialize .zarray: {e}")))
160 }
161
162 pub fn from_json(data: &[u8]) -> Result<Self> {
164 serde_json::from_slice(data).map_err(|e| {
165 IoError::DeserializationError(format!("Failed to deserialize .zarray: {e}"))
166 })
167 }
168
169 pub fn data_type(&self) -> Result<DataType> {
171 DataType::from_v2_dtype(&self.dtype)
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ArrayMetadataV3 {
180 pub zarr_format: u32,
182 pub node_type: String,
184 #[serde(default, skip_serializing_if = "Vec::is_empty")]
186 pub shape: Vec<u64>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub data_type: Option<String>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
192 pub chunk_grid: Option<ChunkGrid>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub chunk_key_encoding: Option<ChunkKeyEncoding>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub fill_value: Option<serde_json::Value>,
199 #[serde(default, skip_serializing_if = "Vec::is_empty")]
201 pub codecs: Vec<CodecMetadata>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub attributes: Option<HashMap<String, serde_json::Value>>,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct GroupMetadataV3 {
210 pub zarr_format: u32,
212 pub node_type: String,
214 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub attributes: Option<HashMap<String, serde_json::Value>>,
217}
218
219impl GroupMetadataV3 {
220 pub fn new() -> Self {
222 Self {
223 zarr_format: 3,
224 node_type: "group".to_string(),
225 attributes: None,
226 }
227 }
228}
229
230impl Default for GroupMetadataV3 {
231 fn default() -> Self {
232 Self::new()
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct ChunkGrid {
239 pub name: String,
241 pub configuration: ChunkGridConfig,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct ChunkGridConfig {
248 pub chunk_shape: Vec<u64>,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct ChunkKeyEncoding {
255 pub name: String,
257 #[serde(default, skip_serializing_if = "Option::is_none")]
259 pub configuration: Option<ChunkKeyEncodingConfig>,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct ChunkKeyEncodingConfig {
265 pub separator: String,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct CodecMetadata {
272 pub name: String,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub configuration: Option<serde_json::Value>,
277}
278
279impl ArrayMetadataV3 {
280 pub fn new_array(
282 shape: Vec<u64>,
283 chunk_shape: Vec<u64>,
284 dtype: DataType,
285 fill_value: serde_json::Value,
286 codecs: Vec<CodecMetadata>,
287 ) -> Self {
288 Self {
289 zarr_format: 3,
290 node_type: "array".to_string(),
291 shape,
292 data_type: Some(dtype.to_v3_name().to_string()),
293 chunk_grid: Some(ChunkGrid {
294 name: "regular".to_string(),
295 configuration: ChunkGridConfig { chunk_shape },
296 }),
297 chunk_key_encoding: Some(ChunkKeyEncoding {
298 name: "default".to_string(),
299 configuration: Some(ChunkKeyEncodingConfig {
300 separator: "/".to_string(),
301 }),
302 }),
303 fill_value: Some(fill_value),
304 codecs,
305 attributes: None,
306 }
307 }
308
309 pub fn to_json(&self) -> Result<Vec<u8>> {
311 serde_json::to_vec_pretty(self)
312 .map_err(|e| IoError::SerializationError(format!("Failed to serialize zarr.json: {e}")))
313 }
314
315 pub fn from_json(data: &[u8]) -> Result<Self> {
317 serde_json::from_slice(data).map_err(|e| {
318 IoError::DeserializationError(format!("Failed to deserialize zarr.json: {e}"))
319 })
320 }
321
322 pub fn data_type_parsed(&self) -> Result<DataType> {
324 let name = self
325 .data_type
326 .as_deref()
327 .ok_or_else(|| IoError::FormatError("Missing data_type in zarr.json".to_string()))?;
328 DataType::from_v3_name(name)
329 }
330
331 pub fn chunk_shape(&self) -> Result<&[u64]> {
333 self.chunk_grid
334 .as_ref()
335 .map(|g| g.configuration.chunk_shape.as_slice())
336 .ok_or_else(|| IoError::FormatError("Missing chunk_grid in zarr.json".to_string()))
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn test_v2_array_metadata_roundtrip() {
346 let meta = ArrayMetadataV2::new(
347 vec![100, 200],
348 vec![10, 20],
349 DataType::Float64,
350 None,
351 serde_json::json!(0.0),
352 );
353 let json = meta.to_json().expect("serialize");
354 let parsed = ArrayMetadataV2::from_json(&json).expect("deserialize");
355 assert_eq!(parsed.shape, vec![100, 200]);
356 assert_eq!(parsed.chunks, vec![10, 20]);
357 assert_eq!(parsed.dtype, "<f8");
358 assert_eq!(parsed.zarr_format, 2);
359 assert_eq!(parsed.order, "C");
360 }
361
362 #[test]
363 fn test_v2_group_metadata() {
364 let meta = GroupMetadataV2::new();
365 let json = serde_json::to_vec(&meta).expect("serialize");
366 let parsed: GroupMetadataV2 = serde_json::from_slice(&json).expect("deserialize");
367 assert_eq!(parsed.zarr_format, 2);
368 }
369
370 #[test]
371 fn test_v3_array_metadata_roundtrip() {
372 let meta = ArrayMetadataV3::new_array(
373 vec![1000, 2000],
374 vec![100, 200],
375 DataType::Int32,
376 serde_json::json!(0),
377 vec![CodecMetadata {
378 name: "bytes".to_string(),
379 configuration: Some(serde_json::json!({"endian": "little"})),
380 }],
381 );
382 let json = meta.to_json().expect("serialize");
383 let parsed = ArrayMetadataV3::from_json(&json).expect("deserialize");
384 assert_eq!(parsed.zarr_format, 3);
385 assert_eq!(parsed.node_type, "array");
386 assert_eq!(parsed.shape, vec![1000, 2000]);
387 assert_eq!(parsed.data_type_parsed().expect("dtype"), DataType::Int32);
388 assert_eq!(parsed.chunk_shape().expect("chunks"), &[100, 200]);
389 assert_eq!(parsed.codecs.len(), 1);
390 }
391
392 #[test]
393 fn test_v3_group_metadata() {
394 let meta = GroupMetadataV3::new();
395 let json = serde_json::to_vec(&meta).expect("serialize");
396 let parsed: GroupMetadataV3 = serde_json::from_slice(&json).expect("deserialize");
397 assert_eq!(parsed.zarr_format, 3);
398 assert_eq!(parsed.node_type, "group");
399 }
400
401 #[test]
402 fn test_v2_metadata_with_compressor() {
403 let comp = CompressorV2 {
404 id: "zstd".to_string(),
405 level: Some(3),
406 extra: HashMap::new(),
407 };
408 let meta = ArrayMetadataV2::new(
409 vec![50],
410 vec![10],
411 DataType::UInt8,
412 Some(comp),
413 serde_json::json!(255),
414 );
415 let json = meta.to_json().expect("serialize");
416 let parsed = ArrayMetadataV2::from_json(&json).expect("deserialize");
417 let c = parsed.compressor.expect("compressor present");
418 assert_eq!(c.id, "zstd");
419 assert_eq!(c.level, Some(3));
420 }
421
422 #[test]
423 fn test_consolidated_metadata() {
424 let mut cm = ConsolidatedMetadata::new();
425 let arr_meta = ArrayMetadataV2::new(
426 vec![10],
427 vec![5],
428 DataType::Float32,
429 None,
430 serde_json::json!(0.0),
431 );
432 cm.insert_array("data", &arr_meta).expect("insert array");
433 let grp_meta = GroupMetadataV2::new();
434 cm.insert_group("", &grp_meta).expect("insert group");
435
436 let json = cm.to_json().expect("serialize");
437 let parsed = ConsolidatedMetadata::from_json(&json).expect("deserialize");
438 assert!(parsed.metadata.contains_key("data/.zarray"));
439 assert!(parsed.metadata.contains_key("/.zgroup"));
440 }
441
442 #[test]
443 fn test_v2_data_type_parsing() {
444 let meta = ArrayMetadataV2::new(
445 vec![10],
446 vec![5],
447 DataType::Int16,
448 None,
449 serde_json::json!(0),
450 );
451 assert_eq!(meta.data_type().expect("parse"), DataType::Int16);
452 }
453}