Skip to main content

icydb_core/db/registry/
registry.rs

1use crate::{
2    db::{
3        data::DataStore,
4        index::IndexStore,
5        registry::{StoreAllocationIdentities, StoreHandle, StoreRegistryError},
6        schema::SchemaStore,
7    },
8    error::InternalError,
9};
10use std::{cell::RefCell, thread::LocalKey};
11
12///
13/// StoreRegistry
14///
15/// StoreRegistry owns the generated mapping from schema `Store` paths to their
16/// row, index, and schema store handles.
17/// It validates registration invariants once at generated wiring time and then
18/// serves cheap immutable lookups during runtime operations.
19///
20
21#[derive(Default)]
22pub struct StoreRegistry {
23    stores: Vec<(&'static str, StoreHandle)>,
24}
25
26impl StoreRegistry {
27    /// Create an empty store registry.
28    #[must_use]
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Iterate registered stores.
34    ///
35    /// Iteration order follows registration order. Semantic result ordering
36    /// must still not depend on this iteration order; callers that need
37    /// deterministic ordering must sort by store path.
38    pub fn iter(&self) -> impl Iterator<Item = (&'static str, StoreHandle)> {
39        self.stores.iter().copied()
40    }
41
42    /// Register a `Store` path to its row/index/schema store triplet.
43    pub fn register_store(
44        &mut self,
45        name: &'static str,
46        data: &'static LocalKey<RefCell<DataStore>>,
47        index: &'static LocalKey<RefCell<IndexStore>>,
48        schema: &'static LocalKey<RefCell<SchemaStore>>,
49    ) -> Result<(), InternalError> {
50        self.register_store_with_allocations(
51            name,
52            data,
53            index,
54            schema,
55            StoreAllocationIdentities::absent(),
56        )
57    }
58
59    /// Register a `Store` path to its row/index/schema store triplet with
60    /// durable allocation identity descriptors.
61    pub fn register_store_with_allocations(
62        &mut self,
63        name: &'static str,
64        data: &'static LocalKey<RefCell<DataStore>>,
65        index: &'static LocalKey<RefCell<IndexStore>>,
66        schema: &'static LocalKey<RefCell<SchemaStore>>,
67        allocations: StoreAllocationIdentities,
68    ) -> Result<(), InternalError> {
69        if self
70            .stores
71            .iter()
72            .any(|(existing_name, _)| *existing_name == name)
73        {
74            return Err(StoreRegistryError::StoreAlreadyRegistered(name.to_string()).into());
75        }
76
77        // Keep one canonical logical store name per physical row/index/schema
78        // store triplet.
79        if let Some(existing_name) =
80            self.stores
81                .iter()
82                .find_map(|(existing_name, existing_handle)| {
83                    (std::ptr::eq(existing_handle.data_store(), data)
84                        && std::ptr::eq(existing_handle.index_store(), index)
85                        && std::ptr::eq(existing_handle.schema_store(), schema))
86                    .then_some(*existing_name)
87                })
88        {
89            return Err(StoreRegistryError::StoreHandleTripletAlreadyRegistered {
90                name: name.to_string(),
91                existing_name: existing_name.to_string(),
92            }
93            .into());
94        }
95
96        self.stores.push((
97            name,
98            StoreHandle::new_with_allocations(data, index, schema, allocations),
99        ));
100
101        Ok(())
102    }
103
104    /// Look up a store handle by path.
105    pub fn try_get_store(&self, path: &str) -> Result<StoreHandle, InternalError> {
106        self.stores
107            .iter()
108            .find_map(|(existing_path, handle)| (*existing_path == path).then_some(*handle))
109            .ok_or_else(|| StoreRegistryError::StoreNotFound(path.to_string()).into())
110    }
111}