use crate::{
db::{
data::DataStore,
index::IndexStore,
journal::JournalTailStore,
registry::{
StoreAllocationIdentities, StoreHandle, StoreRegistryError,
StoreRuntimeStorageCapabilities, StoreRuntimeStorageMode,
},
schema::SchemaStore,
},
error::InternalError,
};
use std::{cell::RefCell, thread::LocalKey};
#[derive(Default)]
pub struct StoreRegistry {
stores: Vec<(&'static str, StoreHandle)>,
}
impl StoreRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn iter(&self) -> impl Iterator<Item = (&'static str, StoreHandle)> {
self.stores.iter().copied()
}
pub fn register_store(
&mut self,
name: &'static str,
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
allocations: StoreAllocationIdentities,
capabilities: StoreRuntimeStorageCapabilities,
) -> Result<(), InternalError> {
self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
if capabilities.storage_mode() == StoreRuntimeStorageMode::Journaled {
return Err(
StoreRegistryError::StoreAllocationCapabilityMismatch(name.to_string()).into(),
);
}
self.stores.push((
name,
StoreHandle::new(data, index, schema, allocations, capabilities),
));
Ok(())
}
#[allow(
clippy::too_many_arguments,
reason = "generated journaled registration adds one journal-tail handle to the existing store triplet"
)]
pub fn register_journaled_store(
&mut self,
name: &'static str,
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
journal: &'static LocalKey<RefCell<JournalTailStore>>,
allocations: StoreAllocationIdentities,
capabilities: StoreRuntimeStorageCapabilities,
) -> Result<(), InternalError> {
self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
if capabilities.storage_mode() != StoreRuntimeStorageMode::Journaled
|| allocations.journal().is_none()
{
return Err(
StoreRegistryError::StoreAllocationCapabilityMismatch(name.to_string()).into(),
);
}
self.stores.push((
name,
StoreHandle::new_journaled(data, index, schema, journal, allocations, capabilities),
));
Ok(())
}
pub fn try_get_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
self.stores
.iter()
.find_map(|(existing_path, handle)| (*existing_path == path).then_some(*handle))
.ok_or_else(|| StoreRegistryError::StoreNotFound(path.to_string()).into())
}
fn validate_register_store_shape(
&self,
name: &'static str,
data: &'static LocalKey<RefCell<DataStore>>,
index: &'static LocalKey<RefCell<IndexStore>>,
schema: &'static LocalKey<RefCell<SchemaStore>>,
allocations: StoreAllocationIdentities,
capabilities: StoreRuntimeStorageCapabilities,
) -> Result<(), InternalError> {
if self
.stores
.iter()
.any(|(existing_name, _)| *existing_name == name)
{
return Err(StoreRegistryError::StoreAlreadyRegistered(name.to_string()).into());
}
if let Some(existing_name) =
self.stores
.iter()
.find_map(|(existing_name, existing_handle)| {
(std::ptr::eq(existing_handle.data_store(), data)
&& std::ptr::eq(existing_handle.index_store(), index)
&& std::ptr::eq(existing_handle.schema_store(), schema))
.then_some(*existing_name)
})
{
return Err(StoreRegistryError::StoreHandleTripletAlreadyRegistered {
name: name.to_string(),
existing_name: existing_name.to_string(),
}
.into());
}
if allocations.allocation_identity_capability() != Some(capabilities.allocation_identity())
|| !allocations.matches_storage_capabilities(capabilities)
{
return Err(
StoreRegistryError::StoreAllocationCapabilityMismatch(name.to_string()).into(),
);
}
Ok(())
}
}