icydb_core/db/registry/
registry.rs1use crate::{
2 db::{
3 data::DataStore,
4 index::IndexStore,
5 journal::JournalTailStore,
6 registry::{
7 StoreAllocationIdentities, StoreHandle, StoreRegistryError,
8 StoreRuntimeStorageCapabilities, StoreRuntimeStorageMode,
9 },
10 schema::SchemaStore,
11 },
12 error::InternalError,
13};
14use std::{cell::RefCell, thread::LocalKey};
15
16#[derive(Default)]
26pub struct StoreRegistry {
27 stores: Vec<(&'static str, StoreHandle)>,
28}
29
30impl StoreRegistry {
31 #[must_use]
33 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn iter(&self) -> impl Iterator<Item = (&'static str, StoreHandle)> {
43 self.stores.iter().copied()
44 }
45
46 pub fn register_store(
54 &mut self,
55 name: &'static str,
56 data: &'static LocalKey<RefCell<DataStore>>,
57 index: &'static LocalKey<RefCell<IndexStore>>,
58 schema: &'static LocalKey<RefCell<SchemaStore>>,
59 allocations: StoreAllocationIdentities,
60 capabilities: StoreRuntimeStorageCapabilities,
61 ) -> Result<(), InternalError> {
62 self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
63 if capabilities.storage_mode() == StoreRuntimeStorageMode::Journaled {
64 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
65 }
66
67 self.stores.push((
68 name,
69 StoreHandle::new(data, index, schema, allocations, capabilities),
70 ));
71
72 Ok(())
73 }
74
75 #[expect(
77 clippy::too_many_arguments,
78 reason = "generated journaled registration adds one journal-tail handle to the existing store triplet"
79 )]
80 pub fn register_journaled_store(
81 &mut self,
82 name: &'static str,
83 data: &'static LocalKey<RefCell<DataStore>>,
84 index: &'static LocalKey<RefCell<IndexStore>>,
85 schema: &'static LocalKey<RefCell<SchemaStore>>,
86 journal: &'static LocalKey<RefCell<JournalTailStore>>,
87 allocations: StoreAllocationIdentities,
88 capabilities: StoreRuntimeStorageCapabilities,
89 ) -> Result<(), InternalError> {
90 self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
91 if capabilities.storage_mode() != StoreRuntimeStorageMode::Journaled
92 || allocations.journal().is_none()
93 {
94 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
95 }
96
97 self.stores.push((
98 name,
99 StoreHandle::new_journaled(data, index, schema, journal, allocations, capabilities),
100 ));
101
102 Ok(())
103 }
104
105 pub fn try_get_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
107 self.stores
108 .iter()
109 .find_map(|(existing_path, handle)| (*existing_path == path).then_some(*handle))
110 .ok_or_else(|| StoreRegistryError::StoreNotFound.into())
111 }
112
113 fn validate_register_store_shape(
114 &self,
115 name: &'static str,
116 data: &'static LocalKey<RefCell<DataStore>>,
117 index: &'static LocalKey<RefCell<IndexStore>>,
118 schema: &'static LocalKey<RefCell<SchemaStore>>,
119 allocations: StoreAllocationIdentities,
120 capabilities: StoreRuntimeStorageCapabilities,
121 ) -> Result<(), InternalError> {
122 if self
123 .stores
124 .iter()
125 .any(|(existing_name, _)| *existing_name == name)
126 {
127 return Err(StoreRegistryError::StoreAlreadyRegistered.into());
128 }
129
130 if self.stores.iter().any(|(_, existing_handle)| {
133 std::ptr::eq(existing_handle.data_store(), data)
134 && std::ptr::eq(existing_handle.index_store(), index)
135 && std::ptr::eq(existing_handle.schema_store(), schema)
136 }) {
137 return Err(StoreRegistryError::StoreHandleTripletAlreadyRegistered.into());
138 }
139
140 if allocations.allocation_identity_capability() != Some(capabilities.allocation_identity())
141 || !allocations.matches_storage_capabilities(capabilities)
142 {
143 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
144 }
145
146 Ok(())
147 }
148}