use std::sync::{Arc, LazyLock};
use derive_more::{Deref, From};
use zarrs_metadata::Configuration;
use zarrs_metadata::v3::MetadataV3;
use zarrs_plugin::{
ExtensionAliases, ExtensionName, MaybeSend, MaybeSync, Plugin, PluginCreateError,
PluginUnsupportedError, RuntimePlugin, RuntimeRegistry, ZarrVersion, ZarrVersion3,
};
use zarrs_storage::StoreKey;
#[derive(Debug, Clone, From, Deref)]
pub struct ChunkKeyEncoding(Arc<dyn ChunkKeyEncodingTraits>);
impl<T: ChunkKeyEncodingTraits + 'static> From<Arc<T>> for ChunkKeyEncoding {
fn from(chunk_key_encoding: Arc<T>) -> Self {
Self(chunk_key_encoding)
}
}
#[derive(Deref)]
pub struct ChunkKeyEncodingPlugin(Plugin<ChunkKeyEncoding, MetadataV3>);
inventory::collect!(ChunkKeyEncodingPlugin);
impl ChunkKeyEncodingPlugin {
pub const fn new<T: ExtensionAliases<ZarrVersion3> + ChunkKeyEncodingTraits>() -> Self {
Self(Plugin::new(T::matches_name, T::create))
}
}
pub type ChunkKeyEncodingRuntimePlugin = RuntimePlugin<ChunkKeyEncoding, MetadataV3>;
pub static CHUNK_KEY_ENCODING_RUNTIME_REGISTRY: LazyLock<
RuntimeRegistry<ChunkKeyEncodingRuntimePlugin>,
> = LazyLock::new(RuntimeRegistry::new);
pub type ChunkKeyEncodingRuntimeRegistryHandle = Arc<ChunkKeyEncodingRuntimePlugin>;
pub fn register_chunk_key_encoding(
plugin: ChunkKeyEncodingRuntimePlugin,
) -> ChunkKeyEncodingRuntimeRegistryHandle {
CHUNK_KEY_ENCODING_RUNTIME_REGISTRY.register(plugin)
}
pub fn unregister_chunk_key_encoding(handle: &ChunkKeyEncodingRuntimeRegistryHandle) -> bool {
CHUNK_KEY_ENCODING_RUNTIME_REGISTRY.unregister(handle)
}
impl ExtensionName for ChunkKeyEncoding {
fn name(&self, version: ZarrVersion) -> Option<std::borrow::Cow<'static, str>> {
self.0.name(version)
}
}
impl ChunkKeyEncoding {
pub fn new<T: ChunkKeyEncodingTraits + 'static>(chunk_key_encoding: T) -> Self {
let chunk_key_encoding: Arc<dyn ChunkKeyEncodingTraits> = Arc::new(chunk_key_encoding);
chunk_key_encoding.into()
}
#[must_use]
pub fn metadata(&self) -> MetadataV3 {
let name = self
.name_v3()
.expect("chunk key encoding must have a V3 name");
let configuration = self.0.configuration();
if configuration.is_empty() {
MetadataV3::new(name.into_owned())
} else {
MetadataV3::new_with_configuration(name.into_owned(), configuration)
}
}
pub fn from_metadata(metadata: &MetadataV3) -> Result<Self, PluginCreateError> {
let name = metadata.name();
{
let result = CHUNK_KEY_ENCODING_RUNTIME_REGISTRY.with_plugins(|plugins| {
for plugin in plugins {
if plugin.match_name(name) {
return Some(plugin.create(metadata));
}
}
None
});
if let Some(result) = result {
return result;
}
}
for plugin in inventory::iter::<ChunkKeyEncodingPlugin> {
if plugin.match_name(name) {
return plugin.create(metadata);
}
}
Err(PluginUnsupportedError::new(
metadata.name().to_string(),
"chunk key encoding".to_string(),
)
.into())
}
}
impl<T> From<T> for ChunkKeyEncoding
where
T: ChunkKeyEncodingTraits + 'static,
{
fn from(chunk_key_encoding: T) -> Self {
Self::new(chunk_key_encoding)
}
}
pub trait ChunkKeyEncodingTraits: ExtensionName + core::fmt::Debug + MaybeSend + MaybeSync {
fn create(metadata: &MetadataV3) -> Result<ChunkKeyEncoding, PluginCreateError>
where
Self: Sized;
fn configuration(&self) -> Configuration;
fn encode(&self, chunk_grid_indices: &[u64]) -> StoreKey;
}