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