use std::collections::BTreeMap;
use std::sync::OnceLock;
use inventory::iter;
use crate::ValueBytesCodecDescriptor;
use crate::ValueBytesCodecRegistrationFactory;
use crate::ValueCodecId;
use crate::ValueCodecRegistration;
use crate::ValueCodecRegistryError;
use crate::ValueStringCodecDescriptor;
use crate::ValueStringCodecRegistrationFactory;
pub type ValueStringCodecRegistry = ValueCodecRegistry<ValueStringCodecDescriptor>;
pub type ValueBytesCodecRegistry = ValueCodecRegistry<ValueBytesCodecDescriptor>;
#[derive(Debug)]
pub struct ValueCodecRegistry<D: 'static> {
registrations: Box<[ValueCodecRegistration<D>]>,
indices: BTreeMap<ValueCodecId, usize>,
}
impl<D: Copy + 'static> ValueCodecRegistry<D> {
pub fn from_registrations(
registrations: impl IntoIterator<Item = &'static ValueCodecRegistration<D>>,
) -> Result<Self, ValueCodecRegistryError> {
Self::build(registrations.into_iter().copied().collect())
}
#[must_use]
#[inline]
pub fn empty() -> Self {
Self {
registrations: Box::new([]),
indices: BTreeMap::new(),
}
}
fn build(mut registrations: Vec<ValueCodecRegistration<D>>) -> Result<Self, ValueCodecRegistryError> {
registrations.sort_by_key(ValueCodecRegistration::id);
for (index, pair) in registrations.windows(2).enumerate() {
if pair[0].id() == pair[1].id() {
let id = pair[0].id();
let mut sources = registrations[index..]
.iter()
.take_while(|registration| registration.id() == id)
.map(ValueCodecRegistration::source)
.collect::<Vec<_>>();
sources.sort_unstable();
return Err(ValueCodecRegistryError::DuplicateId {
id: id.as_str(),
sources,
});
}
}
let indices = registrations
.iter()
.enumerate()
.map(|(index, registration)| (registration.id(), index))
.collect();
Ok(Self {
registrations: registrations.into_boxed_slice(),
indices,
})
}
#[must_use]
#[inline]
pub fn get(&self, id: &str) -> Option<&ValueCodecRegistration<D>> {
let index = *self.indices.get(id)?;
self.registrations.get(index)
}
#[must_use]
#[inline]
pub fn registrations(&self) -> &[ValueCodecRegistration<D>] {
&self.registrations
}
}
impl ValueStringCodecRegistry {
pub fn try_global() -> Result<&'static Self, ValueCodecRegistryError> {
static REGISTRY: OnceLock<Result<ValueStringCodecRegistry, ValueCodecRegistryError>> = OnceLock::new();
match REGISTRY.get_or_init(|| {
let registrations = iter::<ValueStringCodecRegistrationFactory>
.into_iter()
.map(|factory| (factory.0)())
.collect();
Self::build(registrations)
}) {
Ok(registry) => Ok(registry),
Err(error) => Err(error.clone()),
}
}
#[must_use]
pub fn global() -> &'static Self {
Self::try_global().unwrap_or_else(|error| panic!("invalid global value string codec registry: {error}"))
}
}
impl ValueBytesCodecRegistry {
pub fn try_global() -> Result<&'static Self, ValueCodecRegistryError> {
static REGISTRY: OnceLock<Result<ValueBytesCodecRegistry, ValueCodecRegistryError>> = OnceLock::new();
match REGISTRY.get_or_init(|| {
let registrations = iter::<ValueBytesCodecRegistrationFactory>
.into_iter()
.map(|factory| (factory.0)())
.collect();
Self::build(registrations)
}) {
Ok(registry) => Ok(registry),
Err(error) => Err(error.clone()),
}
}
#[must_use]
pub fn global() -> &'static Self {
Self::try_global().unwrap_or_else(|error| panic!("invalid global value bytes codec registry: {error}"))
}
}