icydb_core/db/
mod.rs

1pub mod executor;
2pub mod primitives;
3pub mod query;
4pub mod response;
5pub mod store;
6
7use crate::{
8    db::{
9        executor::{Context, DeleteExecutor, LoadExecutor, SaveExecutor, UpsertExecutor},
10        store::{DataStoreRegistry, IndexStoreRegistry},
11    },
12    runtime_error::RuntimeError,
13    traits::{CanisterKind, EntityKind, FromKey},
14};
15use std::{marker::PhantomData, thread::LocalKey};
16
17///
18/// Db
19///
20/// A handle to the set of stores registered for a specific canister domain.
21///
22/// - `C` is the [`CanisterKind`] (schema canister marker).
23///
24/// The `Db` acts as the entry point for querying, saving, and deleting entities
25/// within a single canister's store registry.
26///
27
28pub struct Db<C: CanisterKind> {
29    data: &'static LocalKey<DataStoreRegistry>,
30    index: &'static LocalKey<IndexStoreRegistry>,
31    _marker: PhantomData<C>,
32}
33
34impl<C: CanisterKind> Db<C> {
35    #[must_use]
36    pub const fn new(
37        data: &'static LocalKey<DataStoreRegistry>,
38        index: &'static LocalKey<IndexStoreRegistry>,
39    ) -> Self {
40        Self {
41            data,
42            index,
43            _marker: PhantomData,
44        }
45    }
46
47    #[must_use]
48    pub const fn context<E>(&self) -> Context<'_, E>
49    where
50        E: EntityKind<Canister = C>,
51    {
52        Context::new(self)
53    }
54
55    /// Run a closure with read access to the data store registry.
56    pub fn with_data<R>(&self, f: impl FnOnce(&DataStoreRegistry) -> R) -> R {
57        self.data.with(|reg| f(reg))
58    }
59
60    /// Run a closure with read access to the index store registry.
61    pub fn with_index<R>(&self, f: impl FnOnce(&IndexStoreRegistry) -> R) -> R {
62        self.index.with(|reg| f(reg))
63    }
64}
65
66// Manual Copy + Clone implementations.
67// Safe because Db only contains &'static LocalKey<_> handles,
68// duplicating them does not duplicate the contents.
69impl<C: CanisterKind> Copy for Db<C> {}
70
71impl<C: CanisterKind> Clone for Db<C> {
72    fn clone(&self) -> Self {
73        *self
74    }
75}
76
77///
78/// DbSession
79/// Database handle plus a debug flag that controls executor verbosity.
80///
81
82pub struct DbSession<C: CanisterKind> {
83    db: Db<C>,
84    debug: bool,
85}
86
87impl<C: CanisterKind> DbSession<C> {
88    #[must_use]
89    /// Create a new session scoped to the provided database.
90    pub const fn new(db: Db<C>) -> Self {
91        Self { db, debug: false }
92    }
93
94    #[must_use]
95    /// Enable debug logging for subsequent queries in this session.
96    pub const fn debug(mut self) -> Self {
97        self.debug = true;
98        self
99    }
100
101    //
102    // Low-level executors
103    //
104
105    /// Get a [`LoadExecutor`] for building and executing queries that read entities.
106    #[must_use]
107    pub const fn load<E>(&self) -> LoadExecutor<E>
108    where
109        E: EntityKind<Canister = C>,
110    {
111        LoadExecutor::new(self.db, self.debug)
112    }
113
114    /// Get a [`SaveExecutor`] for inserting or updating entities.
115    ///
116    /// Normally you will use the higher-level `create/replace/update` shortcuts instead.
117    #[must_use]
118    pub const fn save<E>(&self) -> SaveExecutor<E>
119    where
120        E: EntityKind<Canister = C>,
121    {
122        SaveExecutor::new(self.db, self.debug)
123    }
124
125    /// Get an [`UpsertExecutor`] for inserting or updating by a unique index.
126    #[must_use]
127    pub const fn upsert<E>(&self) -> UpsertExecutor<E>
128    where
129        E: EntityKind<Canister = C>,
130        E::PrimaryKey: FromKey,
131    {
132        UpsertExecutor::new(self.db, self.debug)
133    }
134
135    /// Get a [`DeleteExecutor`] for deleting entities by key or query.
136    #[must_use]
137    pub const fn delete<E>(&self) -> DeleteExecutor<E>
138    where
139        E: EntityKind<Canister = C>,
140    {
141        DeleteExecutor::new(self.db, self.debug)
142    }
143
144    //
145    // High-level save shortcuts
146    //
147
148    /// Insert a new entity, returning the stored value.
149    pub fn insert<E>(&self, entity: E) -> Result<E, RuntimeError>
150    where
151        E: EntityKind<Canister = C>,
152    {
153        self.save::<E>().insert(entity)
154    }
155
156    /// Insert multiple entities, returning stored values.
157    pub fn insert_many<E>(
158        &self,
159        entities: impl IntoIterator<Item = E>,
160    ) -> Result<Vec<E>, RuntimeError>
161    where
162        E: EntityKind<Canister = C>,
163    {
164        self.save::<E>().insert_many(entities)
165    }
166
167    /// Replace an existing entity or insert it if it does not yet exist.
168    pub fn replace<E>(&self, entity: E) -> Result<E, RuntimeError>
169    where
170        E: EntityKind<Canister = C>,
171    {
172        self.save::<E>().replace(entity)
173    }
174
175    /// Replace multiple entities, inserting if missing.
176    pub fn replace_many<E>(
177        &self,
178        entities: impl IntoIterator<Item = E>,
179    ) -> Result<Vec<E>, RuntimeError>
180    where
181        E: EntityKind<Canister = C>,
182    {
183        self.save::<E>().replace_many(entities)
184    }
185
186    /// Partially update an existing entity.
187    pub fn update<E>(&self, entity: E) -> Result<E, RuntimeError>
188    where
189        E: EntityKind<Canister = C>,
190    {
191        self.save::<E>().update(entity)
192    }
193
194    /// Partially update multiple existing entities.
195    pub fn update_many<E>(
196        &self,
197        entities: impl IntoIterator<Item = E>,
198    ) -> Result<Vec<E>, RuntimeError>
199    where
200        E: EntityKind<Canister = C>,
201    {
202        self.save::<E>().update_many(entities)
203    }
204
205    /// Insert a new view value for an entity.
206    pub fn insert_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, RuntimeError>
207    where
208        E: EntityKind<Canister = C>,
209    {
210        self.save::<E>().insert_view(view)
211    }
212
213    /// Replace an existing view or insert it if it does not yet exist.
214    pub fn replace_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, RuntimeError>
215    where
216        E: EntityKind<Canister = C>,
217    {
218        self.save::<E>().replace_view(view)
219    }
220
221    /// Partially update an existing view.
222    pub fn update_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, RuntimeError>
223    where
224        E: EntityKind<Canister = C>,
225    {
226        self.save::<E>().update_view(view)
227    }
228}