Skip to main content

icydb_core/db/query/session/
load.rs

1use crate::{
2    db::{
3        DbSession,
4        query::{
5            Query, QueryError,
6            expr::{FilterExpr, SortExpr},
7            plan::{ExecutablePlan, ExplainPlan},
8            predicate::Predicate,
9        },
10        response::Response,
11    },
12    traits::{CanisterKind, EntityKind, EntityValue, SingletonEntity},
13};
14
15///
16/// SessionLoadQuery
17///
18/// Session-bound load query wrapper.
19/// Owns intent construction and execution routing only.
20/// All result inspection and projection is performed on `Response<E>`.
21///
22
23pub struct SessionLoadQuery<'a, C, E>
24where
25    C: CanisterKind,
26    E: EntityKind<Canister = C>,
27{
28    session: &'a DbSession<C>,
29    query: Query<E>,
30}
31
32impl<'a, C, E> SessionLoadQuery<'a, C, E>
33where
34    C: CanisterKind,
35    E: EntityKind<Canister = C>,
36{
37    pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
38        Self { session, query }
39    }
40
41    // ------------------------------------------------------------------
42    // Intent inspection
43    // ------------------------------------------------------------------
44
45    #[must_use]
46    pub const fn query(&self) -> &Query<E> {
47        &self.query
48    }
49
50    // ------------------------------------------------------------------
51    // Intent builders (pure)
52    // ------------------------------------------------------------------
53
54    #[must_use]
55    pub fn by_key(mut self, key: E::Id) -> Self {
56        self.query = self.query.by_key(key);
57        self
58    }
59
60    #[must_use]
61    pub fn many<I>(mut self, keys: I) -> Self
62    where
63        I: IntoIterator<Item = E::Id>,
64    {
65        self.query = self.query.by_keys(keys);
66        self
67    }
68
69    #[must_use]
70    pub fn filter(mut self, predicate: Predicate) -> Self {
71        self.query = self.query.filter(predicate);
72        self
73    }
74
75    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
76        self.query = self.query.filter_expr(expr)?;
77        Ok(self)
78    }
79
80    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
81        self.query = self.query.sort_expr(expr)?;
82        Ok(self)
83    }
84
85    #[must_use]
86    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
87        self.query = self.query.order_by(field);
88        self
89    }
90
91    #[must_use]
92    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
93        self.query = self.query.order_by_desc(field);
94        self
95    }
96
97    #[must_use]
98    pub fn limit(mut self, limit: u32) -> Self {
99        self.query = self.query.limit(limit);
100        self
101    }
102
103    #[must_use]
104    pub fn offset(mut self, offset: u32) -> Self {
105        self.query = self.query.offset(offset);
106        self
107    }
108
109    // ------------------------------------------------------------------
110    // Planning / diagnostics
111    // ------------------------------------------------------------------
112
113    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
114        self.query.explain()
115    }
116
117    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
118        self.query.plan()
119    }
120
121    // ------------------------------------------------------------------
122    // Execution (single semantic boundary)
123    // ------------------------------------------------------------------
124
125    /// Execute this query using the session's policy settings.
126    pub fn execute(&self) -> Result<Response<E>, QueryError>
127    where
128        E: EntityValue,
129    {
130        self.session.execute_query(self.query())
131    }
132
133    // ------------------------------------------------------------------
134    // Execution terminals — semantic only
135    // ------------------------------------------------------------------
136
137    /// Execute and return whether the result set is empty.
138    pub fn is_empty(&self) -> Result<bool, QueryError>
139    where
140        E: EntityValue,
141    {
142        Ok(self.execute()?.is_empty())
143    }
144
145    /// Execute and return the number of matching rows.
146    pub fn count(&self) -> Result<u32, QueryError>
147    where
148        E: EntityValue,
149    {
150        Ok(self.execute()?.count())
151    }
152
153    /// Execute and require exactly one matching row.
154    pub fn require_one(&self) -> Result<(), QueryError>
155    where
156        E: EntityValue,
157    {
158        self.execute()?.require_one().map_err(QueryError::Response)
159    }
160
161    /// Execute and require at least one matching row.
162    pub fn require_some(&self) -> Result<(), QueryError>
163    where
164        E: EntityValue,
165    {
166        self.execute()?.require_some().map_err(QueryError::Response)
167    }
168}
169
170impl<C, E> SessionLoadQuery<'_, C, E>
171where
172    C: CanisterKind,
173    E: EntityKind<Canister = C> + SingletonEntity,
174{
175    #[must_use]
176    pub fn only(mut self, id: E::Id) -> Self {
177        self.query = self.query.only(id);
178        self
179    }
180}