use std::{error::Error, sync::Arc};
use numcodecs::{DynCodec, ErasedDynCodec, ErasedError};
use serde::Deserializer;
pub trait Registry: 'static + Send + Sync {
type Error: 'static + Send + Sync + Error;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<ErasedDynCodec, Self::Error>;
fn get_codec_typed<'de, T: DynCodec, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<Option<T>, Self::Error> {
self.get_codec(config).map(|codec| codec.downcast().ok())
}
}
impl<R: Registry> Registry for Box<R> {
type Error = R::Error;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<ErasedDynCodec, Self::Error> {
R::get_codec(self, config)
}
fn get_codec_typed<'de, T: DynCodec, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<Option<T>, Self::Error> {
R::get_codec_typed(self, config)
}
}
impl<R: Registry> Registry for Arc<R> {
type Error = R::Error;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<ErasedDynCodec, Self::Error> {
R::get_codec(self, config)
}
fn get_codec_typed<'de, T: DynCodec, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<Option<T>, Self::Error> {
R::get_codec_typed(self, config)
}
}
pub struct ErasedRegistry {
registry: Box<dyn ErasedRegistryDispatch>,
}
impl ErasedRegistry {
pub fn new<T: Registry>(registry: T) -> Self {
Self {
registry: Box::new(registry),
}
}
}
impl Registry for ErasedRegistry {
type Error = ErasedError;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<ErasedDynCodec, Self::Error> {
self.registry
.erased_get_codec(&mut <dyn erased_serde::Deserializer>::erase(config))
}
}
trait ErasedRegistryDispatch: 'static + Send + Sync {
fn erased_get_codec(
&self,
config: &mut dyn erased_serde::Deserializer,
) -> Result<ErasedDynCodec, ErasedError>;
}
impl<T: Registry> ErasedRegistryDispatch for T {
fn erased_get_codec(
&self,
config: &mut dyn erased_serde::Deserializer,
) -> Result<ErasedDynCodec, ErasedError> {
match self.get_codec(config) {
Ok(codec) => Ok(codec),
Err(err) => Err(ErasedError::new(err)),
}
}
}
pub struct GlobalRegistry;
impl GlobalRegistry {
fn get() -> &'static ErasedRegistry {
#[expect(unsafe_code)]
unsafe extern "C" {
#[expect(improper_ctypes)]
safe fn _numcodecs_registry_get_global_registry() -> &'static ErasedRegistry;
}
_numcodecs_registry_get_global_registry()
}
pub fn codec_from_config<'de, D: Deserializer<'de>>(
config: D,
) -> Result<ErasedDynCodec, D::Error> {
Self.get_codec(config).map_err(serde::de::Error::custom)
}
}
impl Registry for GlobalRegistry {
type Error = ErasedError;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<ErasedDynCodec, Self::Error> {
Self::get().get_codec(config)
}
fn get_codec_typed<'de, T: DynCodec, D: Deserializer<'de>>(
&self,
config: D,
) -> Result<Option<T>, Self::Error> {
Self::get().get_codec_typed(config)
}
}
#[macro_export]
macro_rules! export_global {
(static REGISTRY: $ty:ty = $init:expr;) => {
const _: () = {
use std::sync::LazyLock;
use $crate::ErasedRegistry;
static _GLOBAL_REGISTRY: LazyLock<ErasedRegistry> =
LazyLock::new(|| ErasedRegistry::new($init));
#[allow(improper_ctypes, unsafe_code)]
#[unsafe(no_mangle)]
extern "C" fn _numcodecs_registry_get_global_registry() -> &'static ErasedRegistry {
LazyLock::force(&_GLOBAL_REGISTRY)
}
};
};
}
#[derive(Debug, thiserror::Error)]
#[error("codec not found")]
pub struct CodecNotFoundError;
pub struct EmptyRegistry;
impl Registry for EmptyRegistry {
type Error = CodecNotFoundError;
fn get_codec<'de, D: Deserializer<'de>>(
&self,
_config: D,
) -> Result<ErasedDynCodec, Self::Error> {
Err(CodecNotFoundError)
}
fn get_codec_typed<'de, T: DynCodec, D: Deserializer<'de>>(
&self,
_config: D,
) -> Result<Option<T>, Self::Error> {
Err(CodecNotFoundError)
}
}