use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use crate::dtype::Dtype;
pub use tensogram_encodings::ByteOrder;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HashDescriptor {
#[serde(rename = "type")]
pub algorithm: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MaskDescriptor {
pub method: String,
pub offset: u64,
pub length: u64,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub params: BTreeMap<String, ciborium::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct MasksMetadata {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub nan: Option<MaskDescriptor>,
#[serde(rename = "inf+", default, skip_serializing_if = "Option::is_none")]
pub pos_inf: Option<MaskDescriptor>,
#[serde(rename = "inf-", default, skip_serializing_if = "Option::is_none")]
pub neg_inf: Option<MaskDescriptor>,
}
impl MasksMetadata {
pub fn is_empty(&self) -> bool {
self.nan.is_none() && self.pos_inf.is_none() && self.neg_inf.is_none()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataObjectDescriptor {
#[serde(rename = "type")]
pub obj_type: String,
pub ndim: u64,
pub shape: Vec<u64>,
pub strides: Vec<u64>,
pub dtype: Dtype,
#[serde(default = "ByteOrder::native")]
pub byte_order: ByteOrder,
pub encoding: String,
pub filter: String,
pub compression: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub masks: Option<MasksMetadata>,
#[serde(flatten)]
pub params: BTreeMap<String, ciborium::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GlobalMetadata {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub base: Vec<BTreeMap<String, ciborium::Value>>,
#[serde(
rename = "_reserved_",
default,
skip_serializing_if = "BTreeMap::is_empty"
)]
pub reserved: BTreeMap<String, ciborium::Value>,
#[serde(
rename = "_extra_",
default,
skip_serializing_if = "BTreeMap::is_empty"
)]
pub extra: BTreeMap<String, ciborium::Value>,
}
#[derive(Debug, Clone, Default)]
pub struct IndexFrame {
pub offsets: Vec<u64>,
pub lengths: Vec<u64>,
}
#[derive(Debug, Clone)]
pub struct HashFrame {
pub algorithm: String,
pub hashes: Vec<String>,
}
pub type DecodedObject = (DataObjectDescriptor, Vec<u8>);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn masks_metadata_is_empty_detects_every_kind_absent() {
let empty = MasksMetadata::default();
assert!(empty.is_empty());
}
#[test]
fn masks_metadata_is_empty_false_when_any_kind_present() {
let any_mask = MaskDescriptor {
method: "roaring".to_string(),
offset: 0,
length: 1,
params: BTreeMap::new(),
};
let nan_only = MasksMetadata {
nan: Some(any_mask.clone()),
..MasksMetadata::default()
};
let pos_only = MasksMetadata {
pos_inf: Some(any_mask.clone()),
..MasksMetadata::default()
};
let neg_only = MasksMetadata {
neg_inf: Some(any_mask),
..MasksMetadata::default()
};
assert!(!nan_only.is_empty());
assert!(!pos_only.is_empty());
assert!(!neg_only.is_empty());
}
#[test]
fn descriptor_deserialize_defaults_byte_order_to_native() {
let json = r#"{
"type": "ntensor",
"ndim": 1,
"shape": [4],
"strides": [1],
"dtype": "float32",
"encoding": "none",
"filter": "none",
"compression": "none"
}"#;
let desc: DataObjectDescriptor =
serde_json::from_str(json).expect("deserialize should succeed without byte_order");
assert_eq!(desc.byte_order, ByteOrder::native());
}
#[test]
fn descriptor_deserialize_honours_explicit_byte_order() {
for (literal, expected) in [("little", ByteOrder::Little), ("big", ByteOrder::Big)] {
let json = format!(
r#"{{
"type": "ntensor", "ndim": 1, "shape": [4], "strides": [1],
"dtype": "float32", "byte_order": "{literal}",
"encoding": "none", "filter": "none", "compression": "none"
}}"#
);
let desc: DataObjectDescriptor =
serde_json::from_str(&json).expect("deserialize should accept explicit byte_order");
assert_eq!(desc.byte_order, expected);
}
}
}