Skip to main content

icydb_core/db/query/
session.rs

1use crate::{
2    db::{
3        DbSession,
4        query::{
5            Query, QueryError,
6            plan::{ExecutablePlan, ExplainPlan},
7            predicate::Predicate,
8        },
9        response::Response,
10    },
11    traits::{CanisterKind, EntityKind},
12};
13
14///
15/// SessionLoadQuery
16///
17/// Fluent, session-bound load query wrapper that keeps intent pure
18/// while routing execution through the `DbSession` boundary.
19///
20
21pub struct SessionLoadQuery<'a, C: CanisterKind, E: EntityKind<Canister = C>> {
22    session: &'a DbSession<C>,
23    intent: Query<E>,
24}
25
26impl<'a, C: CanisterKind, E: EntityKind<Canister = C>> SessionLoadQuery<'a, C, E> {
27    pub(crate) const fn new(session: &'a DbSession<C>, intent: Query<E>) -> Self {
28        Self { session, intent }
29    }
30
31    /// Return a reference to the underlying intent.
32    #[must_use]
33    pub const fn intent(&self) -> &Query<E> {
34        &self.intent
35    }
36
37    /// Add a predicate, implicitly AND-ing with any existing predicate.
38    #[must_use]
39    pub fn filter(mut self, predicate: Predicate) -> Self {
40        self.intent = self.intent.filter(predicate);
41        self
42    }
43
44    /// Append an ascending sort key.
45    #[must_use]
46    pub fn order_by(mut self, field: &'static str) -> Self {
47        self.intent = self.intent.order_by(field);
48        self
49    }
50
51    /// Append a descending sort key.
52    #[must_use]
53    pub fn order_by_desc(mut self, field: &'static str) -> Self {
54        self.intent = self.intent.order_by_desc(field);
55        self
56    }
57
58    /// Apply a load limit to bound result size.
59    #[must_use]
60    pub fn limit(mut self, limit: u32) -> Self {
61        self.intent = self.intent.limit(limit);
62        self
63    }
64
65    /// Apply a load offset.
66    #[must_use]
67    pub fn offset(mut self, offset: u64) -> Self {
68        self.intent = self.intent.offset(offset);
69        self
70    }
71
72    /// Explain this intent without executing it.
73    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
74        self.intent.explain()
75    }
76
77    /// Plan this intent into an executor-ready plan.
78    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
79        self.intent.plan()
80    }
81
82    /// Execute this intent using the session's policy settings.
83    pub fn execute(&self) -> Result<Response<E>, QueryError> {
84        self.session.execute_query(self.intent())
85    }
86
87    /// Execute a load intent and return all entities.
88    pub fn all(&self) -> Result<Vec<E>, QueryError> {
89        let response = self.execute()?;
90
91        Ok(response.entities())
92    }
93
94    /// Execute a load intent and require exactly one entity.
95    pub fn one(&self) -> Result<E, QueryError> {
96        let response = self.execute()?;
97
98        response.entity().map_err(QueryError::Execute)
99    }
100
101    /// Execute a load intent and return zero or one entity.
102    pub fn one_opt(&self) -> Result<Option<E>, QueryError> {
103        let response = self.execute()?;
104
105        response.try_entity().map_err(QueryError::Execute)
106    }
107}
108
109///
110/// SessionDeleteQuery
111///
112/// Fluent, session-bound delete query wrapper that keeps intent pure
113/// while routing execution through the `DbSession` boundary.
114///
115
116pub struct SessionDeleteQuery<'a, C: CanisterKind, E: EntityKind<Canister = C>> {
117    session: &'a DbSession<C>,
118    intent: Query<E>,
119}
120
121impl<'a, C: CanisterKind, E: EntityKind<Canister = C>> SessionDeleteQuery<'a, C, E> {
122    pub(crate) const fn new(session: &'a DbSession<C>, intent: Query<E>) -> Self {
123        Self { session, intent }
124    }
125
126    /// Return a reference to the underlying intent.
127    #[must_use]
128    pub const fn intent(&self) -> &Query<E> {
129        &self.intent
130    }
131
132    /// Add a predicate, implicitly AND-ing with any existing predicate.
133    #[must_use]
134    pub fn filter(mut self, predicate: Predicate) -> Self {
135        self.intent = self.intent.filter(predicate);
136        self
137    }
138
139    /// Append an ascending sort key.
140    #[must_use]
141    pub fn order_by(mut self, field: &'static str) -> Self {
142        self.intent = self.intent.order_by(field);
143        self
144    }
145
146    /// Append a descending sort key.
147    #[must_use]
148    pub fn order_by_desc(mut self, field: &'static str) -> Self {
149        self.intent = self.intent.order_by_desc(field);
150        self
151    }
152
153    /// Apply a delete limit to bound mutation size.
154    #[must_use]
155    pub fn limit(mut self, limit: u32) -> Self {
156        self.intent = self.intent.limit(limit);
157        self
158    }
159
160    /// Explain this intent without executing it.
161    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
162        self.intent.explain()
163    }
164
165    /// Plan this intent into an executor-ready plan.
166    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
167        self.intent.plan()
168    }
169
170    /// Execute this intent using the session's policy settings.
171    pub fn execute(&self) -> Result<Response<E>, QueryError> {
172        self.session.execute_query(self.intent())
173    }
174
175    /// Execute a delete intent and return the deleted rows.
176    pub fn delete_rows(&self) -> Result<Response<E>, QueryError> {
177        self.execute()
178    }
179}