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(
58 &mut self,
59 name: &'static str,
60 data: &'static LocalKey<RefCell<DataStore>>,
61 index: &'static LocalKey<RefCell<IndexStore>>,
62 schema: &'static LocalKey<RefCell<SchemaStore>>,
63 allocations: StoreAllocationIdentities,
64 capabilities: StoreRuntimeStorageCapabilities,
65 ) -> Result<(), InternalError> {
66 self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
67 if capabilities.storage_mode() != StoreRuntimeStorageMode::Heap {
68 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
69 }
70
71 self.stores.push((
72 name,
73 StoreHandle::new(data, index, schema, allocations, capabilities),
74 ));
75
76 Ok(())
77 }
78
79 #[expect(
82 clippy::too_many_arguments,
83 reason = "generated journaled registration adds one journal-tail handle to the existing store triplet"
84 )]
85 pub fn register_journaled_store(
86 &mut self,
87 name: &'static str,
88 data: &'static LocalKey<RefCell<DataStore>>,
89 index: &'static LocalKey<RefCell<IndexStore>>,
90 schema: &'static LocalKey<RefCell<SchemaStore>>,
91 journal: &'static LocalKey<RefCell<JournalTailStore>>,
92 allocations: StoreAllocationIdentities,
93 capabilities: StoreRuntimeStorageCapabilities,
94 ) -> Result<(), InternalError> {
95 self.validate_register_store_shape(name, data, index, schema, allocations, capabilities)?;
96 if capabilities.storage_mode() != StoreRuntimeStorageMode::Journaled
97 || allocations.journal().is_none()
98 {
99 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
100 }
101 if self.stores.iter().any(|(_, existing)| {
102 existing
103 .journal_tail_store()
104 .is_some_and(|existing_journal| std::ptr::eq(existing_journal, journal))
105 }) {
106 return Err(StoreRegistryError::StoreHandleAlreadyRegistered.into());
107 }
108
109 self.stores.push((
110 name,
111 StoreHandle::new_journaled(data, index, schema, journal, allocations, capabilities),
112 ));
113
114 Ok(())
115 }
116
117 pub fn try_get_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
119 self.stores
120 .iter()
121 .find_map(|(existing_path, handle)| (*existing_path == path).then_some(*handle))
122 .ok_or_else(|| StoreRegistryError::StoreNotFound.into())
123 }
124
125 fn validate_register_store_shape(
126 &self,
127 name: &'static str,
128 data: &'static LocalKey<RefCell<DataStore>>,
129 index: &'static LocalKey<RefCell<IndexStore>>,
130 schema: &'static LocalKey<RefCell<SchemaStore>>,
131 allocations: StoreAllocationIdentities,
132 capabilities: StoreRuntimeStorageCapabilities,
133 ) -> Result<(), InternalError> {
134 if self
135 .stores
136 .iter()
137 .any(|(existing_name, _)| *existing_name == name)
138 {
139 return Err(StoreRegistryError::StoreAlreadyRegistered.into());
140 }
141
142 if self.stores.iter().any(|(_, existing_handle)| {
145 std::ptr::eq(existing_handle.data_store(), data)
146 || std::ptr::eq(existing_handle.index_store(), index)
147 || std::ptr::eq(existing_handle.schema_store(), schema)
148 }) {
149 return Err(StoreRegistryError::StoreHandleAlreadyRegistered.into());
150 }
151
152 if allocations.allocation_identity_capability() != Some(capabilities.allocation_identity())
153 || !allocations.matches_storage_capabilities(capabilities)
154 {
155 return Err(StoreRegistryError::StoreAllocationCapabilityMismatch.into());
156 }
157
158 Ok(())
159 }
160}