icydb_core/db/
mod.rs

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