1use derive_more::{
16 derive::{Display, From},
17 Deref, Into,
18};
19use serde::{de::DeserializeOwned, Deserialize, Serialize};
20
21mod array;
22
23pub mod v3;
25
26pub mod v2;
28
29pub use array::{
30 ArrayShape, ChunkKeySeparator, ChunkShape, DimensionName, Endianness, IntoDimensionName,
31};
32use thiserror::Error;
33
34#[derive(Deserialize, Serialize, Clone, PartialEq, Debug, Display, From)]
36#[serde(untagged)]
37#[allow(clippy::large_enum_variant)]
38pub enum ArrayMetadata {
39 V3(v3::ArrayMetadataV3),
41 V2(v2::ArrayMetadataV2),
43}
44
45impl ArrayMetadata {
46 #[allow(clippy::missing_panics_doc)]
48 #[must_use]
49 pub fn to_string_pretty(&self) -> String {
50 serde_json::to_string_pretty(self).expect("array metadata is valid JSON")
51 }
52}
53
54impl TryFrom<&str> for ArrayMetadata {
55 type Error = serde_json::Error;
56 fn try_from(metadata_json: &str) -> Result<Self, Self::Error> {
57 serde_json::from_str::<Self>(metadata_json)
58 }
59}
60
61#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Debug, Display, From)]
63#[serde(untagged)]
64pub enum GroupMetadata {
65 V3(v3::GroupMetadataV3),
67 V2(v2::GroupMetadataV2),
69}
70
71impl GroupMetadata {
72 #[allow(clippy::missing_panics_doc)]
74 #[must_use]
75 pub fn to_string_pretty(&self) -> String {
76 serde_json::to_string_pretty(self).expect("group metadata is valid JSON")
77 }
78}
79
80impl TryFrom<&str> for GroupMetadata {
81 type Error = serde_json::Error;
82 fn try_from(metadata_json: &str) -> Result<Self, Self::Error> {
83 serde_json::from_str::<Self>(metadata_json)
84 }
85}
86
87#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
89#[serde(untagged)]
90#[allow(clippy::large_enum_variant)]
91pub enum NodeMetadata {
92 Array(ArrayMetadata),
94
95 Group(GroupMetadata),
97}
98
99impl NodeMetadata {
100 #[allow(clippy::missing_panics_doc)]
102 #[must_use]
103 pub fn to_string_pretty(&self) -> String {
104 serde_json::to_string_pretty(self).expect("node metadata is valid JSON")
105 }
106}
107
108#[derive(Copy, Clone, Debug, Eq, PartialEq)]
110pub enum DataTypeSize {
111 Fixed(usize),
113 Variable,
117}
118
119#[derive(Default, Serialize, Deserialize, Debug, Clone, Deref, From, Into, Eq, PartialEq)]
121pub struct Configuration(serde_json::Map<String, serde_json::Value>);
122
123impl<T: ConfigurationSerialize> From<T> for Configuration {
124 fn from(value: T) -> Self {
125 match serde_json::to_value(value) {
126 Ok(serde_json::Value::Object(configuration)) => configuration.into(),
127 _ => {
128 panic!("the configuration could not be converted to a JSON object")
129 }
130 }
131 }
132}
133
134pub trait ConfigurationSerialize: Serialize + DeserializeOwned {
138 fn try_from_configuration(configuration: Configuration) -> Result<Self, serde_json::Error> {
143 serde_json::from_value(serde_json::Value::Object(configuration.0))
144 }
145}
146
147#[derive(Clone, Debug, Error, From)]
149#[error("{name} is unsupported, configuration: {configuration:?}")]
150pub struct ConfigurationError {
151 name: String,
152 configuration: Option<Configuration>,
153}
154
155impl ConfigurationError {
156 #[must_use]
158 pub fn new(name: String, configuration: Option<Configuration>) -> Self {
159 Self {
160 name,
161 configuration,
162 }
163 }
164
165 #[must_use]
167 pub fn name(&self) -> &str {
168 &self.name
169 }
170
171 #[must_use]
173 pub const fn configuration(&self) -> Option<&Configuration> {
174 self.configuration.as_ref()
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use v3::{AdditionalFieldV3, AdditionalFieldsV3, MetadataV3};
182
183 #[test]
184 fn metadata() {
185 let metadata = MetadataV3::try_from(r#""bytes""#);
186 assert!(metadata.is_ok());
187 assert_eq!(metadata.unwrap().to_string(), r#"bytes"#);
188 assert!(MetadataV3::try_from(r#"{ "name": "bytes" }"#).is_ok());
189 let metadata =
190 MetadataV3::try_from(r#"{ "name": "bytes", "configuration": { "endian": "little" } }"#);
191 assert!(metadata.is_ok());
192 let metadata = metadata.unwrap();
193 assert_eq!(metadata.to_string(), r#"bytes {"endian":"little"}"#);
194 assert_eq!(metadata.name(), "bytes");
195 assert!(metadata.configuration().is_some());
196 let configuration = metadata.configuration().unwrap();
197 assert!(configuration.contains_key("endian"));
198 assert_eq!(
199 configuration.get("endian").unwrap().as_str().unwrap(),
200 "little"
201 );
202 assert_eq!(
203 MetadataV3::try_from(r#"{ "name": "bytes", "invalid": { "endian": "little" } }"#)
204 .unwrap_err()
205 .to_string(),
206 r#"Expected metadata "<name>" or {"name":"<name>"} or {"name":"<name>","configuration":{}}"#
207 );
208 let metadata =
209 MetadataV3::try_from(r#"{ "name": "bytes", "configuration": { "endian": "little" } }"#)
210 .unwrap();
211 let mut configuration = serde_json::Map::new();
212 configuration.insert("endian".to_string(), "little".into());
213 assert_eq!(metadata.configuration(), Some(&configuration.into()));
214 }
215
216 #[test]
217 fn additional_fields_constructors() {
218 let additional_field = serde_json::Map::new();
219 let additional_field: AdditionalFieldV3 = additional_field.into();
220 assert!(additional_field.must_understand());
221 assert!(
222 additional_field.as_value() == &serde_json::Value::Object(serde_json::Map::default())
223 );
224 assert!(serde_json::to_string(&additional_field).unwrap() == r#"{"must_understand":true}"#);
225
226 let additional_field = AdditionalFieldV3::new("test", true);
227 assert!(additional_field.must_understand());
228 assert!(additional_field.as_value() == &serde_json::Value::String("test".to_string()));
229 assert!(serde_json::to_string(&additional_field).unwrap() == r#""test""#);
230
231 let additional_field = AdditionalFieldV3::new(123, false);
232 assert!(!additional_field.must_understand());
233 assert!(
234 additional_field.as_value()
235 == &serde_json::Value::Number(serde_json::Number::from(123))
236 );
237 assert!(serde_json::to_string(&additional_field).unwrap() == "123");
238 }
239
240 #[test]
241 fn additional_fields_valid() {
242 let json = r#"{
243 "unknown_field": {
244 "key": "value",
245 "must_understand": false
246 },
247 "unsupported_field_1": {
248 "key": "value",
249 "must_understand": true
250 },
251 "unsupported_field_2": {
252 "key": "value"
253 },
254 "unsupported_field_3": [],
255 "unsupported_field_4": "test"
256 }"#;
257 let additional_fields = serde_json::from_str::<AdditionalFieldsV3>(json).unwrap();
258 assert!(additional_fields.len() == 5);
259 assert!(!additional_fields["unknown_field"].must_understand());
260 assert!(additional_fields["unsupported_field_1"].must_understand());
261 assert!(additional_fields["unsupported_field_2"].must_understand());
262 assert!(additional_fields["unsupported_field_3"].must_understand());
263 assert!(additional_fields["unsupported_field_4"].must_understand());
264 }
265}