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::{Context, DeleteExecutor, ExecutorError, LoadExecutor, SaveExecutor},
11        query::QueryError,
12        response::ResponseError,
13        store::{DataStoreRegistry, IndexStoreRegistry, StoreError},
14    },
15    serialize::SerializeError,
16    traits::{CanisterKind, EntityKind},
17    visitor::VisitorError,
18};
19use std::{marker::PhantomData, thread::LocalKey};
20use thiserror::Error as ThisError;
21
22///
23/// DbError
24///
25
26#[derive(Debug, ThisError)]
27pub enum DbError {
28    #[error(transparent)]
29    ExecutorError(#[from] ExecutorError),
30
31    #[error(transparent)]
32    QueryError(#[from] QueryError),
33
34    #[error(transparent)]
35    ResponseError(#[from] ResponseError),
36
37    #[error(transparent)]
38    SerializeError(#[from] SerializeError),
39
40    #[error(transparent)]
41    StoreError(#[from] StoreError),
42
43    #[error(transparent)]
44    VisitorError(#[from] VisitorError),
45}
46
47///
48/// Db
49///
50/// A handle to the set of stores registered for a specific canister domain.
51///
52/// - `C` is the [`CanisterKind`] (schema canister marker).
53///
54/// The `Db` acts as the entry point for querying, saving, and deleting entities
55/// within a single canister's store registry.
56///
57
58pub struct Db<C: CanisterKind> {
59    data: &'static LocalKey<DataStoreRegistry>,
60    index: &'static LocalKey<IndexStoreRegistry>,
61    _marker: PhantomData<C>,
62}
63
64impl<C: CanisterKind> Db<C> {
65    #[must_use]
66    pub const fn new(
67        data: &'static LocalKey<DataStoreRegistry>,
68        index: &'static LocalKey<IndexStoreRegistry>,
69    ) -> Self {
70        Self {
71            data,
72            index,
73            _marker: PhantomData,
74        }
75    }
76
77    #[must_use]
78    pub const fn context<E>(&self) -> Context<'_, E>
79    where
80        E: EntityKind<Canister = C>,
81    {
82        Context::new(self)
83    }
84
85    pub fn with_data<R>(&self, f: impl FnOnce(&DataStoreRegistry) -> R) -> R {
86        self.data.with(|reg| f(reg))
87    }
88
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 plus a debug boolean, as we don't want to store the bool
108/// inside the database handle
109///
110
111pub struct DbSession<C: CanisterKind> {
112    db: Db<C>,
113    debug: bool,
114}
115
116impl<C: CanisterKind> DbSession<C> {
117    #[must_use]
118    pub const fn new(db: Db<C>) -> Self {
119        Self { db, debug: false }
120    }
121
122    #[must_use]
123    pub const fn debug(mut self) -> Self {
124        self.debug = true;
125        self
126    }
127
128    //
129    // Low-level executors
130    //
131
132    /// Get a [`LoadExecutor`] for building and executing queries that read entities.
133    #[must_use]
134    pub const fn load<E>(&self) -> LoadExecutor<E>
135    where
136        E: EntityKind<Canister = C>,
137    {
138        LoadExecutor::new(self.db, self.debug)
139    }
140
141    /// Get a [`SaveExecutor`] for inserting or updating entities.
142    ///
143    /// Normally you will use the higher-level `create/replace/update` shortcuts instead.
144    #[must_use]
145    pub const fn save<E>(&self) -> SaveExecutor<E>
146    where
147        E: EntityKind<Canister = C>,
148    {
149        SaveExecutor::new(self.db, self.debug)
150    }
151
152    /// Get a [`DeleteExecutor`] for deleting entities by key or query.
153    #[must_use]
154    pub const fn delete<E>(&self) -> DeleteExecutor<E>
155    where
156        E: EntityKind<Canister = C>,
157    {
158        DeleteExecutor::new(self.db, self.debug)
159    }
160
161    //
162    // High-level save shortcuts
163    //
164
165    pub fn insert<E>(&self, entity: E) -> Result<E, Error>
166    where
167        E: EntityKind<Canister = C>,
168    {
169        self.save::<E>().insert(entity)
170    }
171
172    pub fn replace<E>(&self, entity: E) -> Result<E, Error>
173    where
174        E: EntityKind<Canister = C>,
175    {
176        self.save::<E>().replace(entity)
177    }
178
179    pub fn update<E>(&self, entity: E) -> Result<E, Error>
180    where
181        E: EntityKind<Canister = C>,
182    {
183        self.save::<E>().update(entity)
184    }
185
186    pub fn insert_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, Error>
187    where
188        E: EntityKind<Canister = C>,
189    {
190        self.save::<E>().insert_view::<E::ViewType>(view)
191    }
192
193    pub fn replace_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, Error>
194    where
195        E: EntityKind<Canister = C>,
196    {
197        self.save::<E>().replace_view::<E::ViewType>(view)
198    }
199
200    pub fn update_view<E>(&self, view: E::ViewType) -> Result<E::ViewType, Error>
201    where
202        E: EntityKind<Canister = C>,
203    {
204        self.save::<E>().update_view::<E::ViewType>(view)
205    }
206}