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