use itertools::Itertools;
use zarrs_chunk_key_encoding::{ChunkKeyEncoding, ChunkKeyEncodingPlugin, ChunkKeyEncodingTraits};
use zarrs_metadata::v3::MetadataV3;
use zarrs_metadata::{ChunkKeySeparator, Configuration};
pub use zarrs_metadata_ext::chunk_key_encoding::v2::V2ChunkKeyEncodingConfiguration;
use zarrs_plugin::PluginCreateError;
use zarrs_storage::StoreKey;
zarrs_plugin::impl_extension_aliases!(V2ChunkKeyEncoding, v3: "v2");
inventory::submit! {
ChunkKeyEncodingPlugin::new::<V2ChunkKeyEncoding>()
}
#[derive(Debug, Clone)]
pub struct V2ChunkKeyEncoding {
separator: ChunkKeySeparator,
}
impl V2ChunkKeyEncoding {
#[must_use]
pub const fn new(separator: ChunkKeySeparator) -> Self {
Self { separator }
}
#[must_use]
pub const fn new_dot() -> Self {
Self {
separator: ChunkKeySeparator::Dot,
}
}
#[must_use]
pub const fn new_slash() -> Self {
Self {
separator: ChunkKeySeparator::Slash,
}
}
}
impl Default for V2ChunkKeyEncoding {
fn default() -> Self {
Self {
separator: ChunkKeySeparator::Dot,
}
}
}
impl ChunkKeyEncodingTraits for V2ChunkKeyEncoding {
fn create(metadata: &MetadataV3) -> Result<ChunkKeyEncoding, PluginCreateError> {
let configuration: V2ChunkKeyEncodingConfiguration = metadata.to_typed_configuration()?;
let v2 = V2ChunkKeyEncoding::new(configuration.separator);
Ok(v2.into())
}
fn configuration(&self) -> Configuration {
V2ChunkKeyEncodingConfiguration {
separator: self.separator,
}
.into()
}
fn encode(&self, chunk_grid_indices: &[u64]) -> StoreKey {
let key = if chunk_grid_indices.is_empty() {
'0'.to_string()
} else {
let mut separator_str: [u8; 4] = [0; 4];
let separator_char: char = self.separator.into();
let separator_str: &str = separator_char.encode_utf8(&mut separator_str);
let mut buffers = vec![itoa::Buffer::new(); chunk_grid_indices.len()];
chunk_grid_indices
.iter()
.zip(&mut buffers)
.map(|(&n, buffer)| buffer.format(n))
.join(separator_str)
};
unsafe { StoreKey::new_unchecked(key) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::node::{NodePath, data_key};
#[test]
fn slash_nd() {
let chunk_key_encoding: ChunkKeyEncoding = V2ChunkKeyEncoding::new_slash().into();
let key = data_key(&NodePath::root(), &chunk_key_encoding.encode(&[1, 23, 45]));
assert_eq!(key, StoreKey::new("1/23/45").unwrap());
}
#[test]
fn dot_nd() {
let chunk_key_encoding: ChunkKeyEncoding = V2ChunkKeyEncoding::new_dot().into();
let key = data_key(&NodePath::root(), &chunk_key_encoding.encode(&[1, 23, 45]));
assert_eq!(key, StoreKey::new("1.23.45").unwrap());
}
#[test]
fn slash_scalar() {
let chunk_key_encoding: ChunkKeyEncoding = V2ChunkKeyEncoding::new_slash().into();
let key = data_key(&NodePath::root(), &chunk_key_encoding.encode(&[]));
assert_eq!(key, StoreKey::new("0").unwrap());
}
#[test]
fn dot_scalar() {
let chunk_key_encoding: ChunkKeyEncoding = V2ChunkKeyEncoding::new_dot().into();
let key = data_key(&NodePath::root(), &chunk_key_encoding.encode(&[]));
assert_eq!(key, StoreKey::new("0").unwrap());
}
}