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