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