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