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::{Id, 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    /// Set the access path to a single entity identity.
56    #[must_use]
57    pub fn by_id(mut self, id: Id<E>) -> Self {
58        self.query = self.query.by_id(id.into_storage_key());
59        self
60    }
61
62    /// Set the access path to multiple entity identities.
63    #[must_use]
64    pub fn by_ids<I>(mut self, ids: I) -> Self
65    where
66        I: IntoIterator<Item = Id<E>>,
67    {
68        self.query = self.query.by_ids(ids.into_iter().map(Id::into_storage_key));
69        self
70    }
71
72    // ------------------------------------------------------------------
73    // Reference-based intent builders
74    // ------------------------------------------------------------------
75
76    /// Resolve a semantic entity reference.
77    #[must_use]
78    pub fn by_ref(mut self, r: Ref<E>) -> Self {
79        // Core is allowed to inspect / resolve refs
80        let key = r.into_storage_key(); // pub(crate)
81        self.query = self.query.by_id(key);
82        self
83    }
84
85    #[must_use]
86    pub fn by_refs<I>(mut self, refs: I) -> Self
87    where
88        I: IntoIterator<Item = Ref<E>>,
89    {
90        let keys = refs.into_iter().map(Ref::into_storage_key);
91        self.query = self.query.by_ids(keys);
92        self
93    }
94
95    // ------------------------------------------------------------------
96    // Query Refinement
97    // ------------------------------------------------------------------
98
99    #[must_use]
100    pub fn filter(mut self, predicate: Predicate) -> Self {
101        self.query = self.query.filter(predicate);
102        self
103    }
104
105    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
106        self.query = self.query.filter_expr(expr)?;
107        Ok(self)
108    }
109
110    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
111        self.query = self.query.sort_expr(expr)?;
112        Ok(self)
113    }
114
115    #[must_use]
116    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
117        self.query = self.query.order_by(field);
118        self
119    }
120
121    #[must_use]
122    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
123        self.query = self.query.order_by_desc(field);
124        self
125    }
126
127    #[must_use]
128    pub fn limit(mut self, limit: u32) -> Self {
129        self.query = self.query.limit(limit);
130        self
131    }
132
133    #[must_use]
134    pub fn offset(mut self, offset: u32) -> Self {
135        self.query = self.query.offset(offset);
136        self
137    }
138
139    // ------------------------------------------------------------------
140    // Planning / diagnostics
141    // ------------------------------------------------------------------
142
143    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
144        self.query.explain()
145    }
146
147    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
148        self.query.plan()
149    }
150
151    // ------------------------------------------------------------------
152    // Execution (single semantic boundary)
153    // ------------------------------------------------------------------
154
155    /// Execute this query using the session's policy settings.
156    pub fn execute(&self) -> Result<Response<E>, QueryError>
157    where
158        E: EntityValue,
159    {
160        self.session.execute_query(self.query())
161    }
162
163    // ------------------------------------------------------------------
164    // Execution terminals — semantic only
165    // ------------------------------------------------------------------
166
167    /// Execute and return whether the result set is empty.
168    pub fn is_empty(&self) -> Result<bool, QueryError>
169    where
170        E: EntityValue,
171    {
172        Ok(self.execute()?.is_empty())
173    }
174
175    /// Execute and return the number of matching rows.
176    pub fn count(&self) -> Result<u32, QueryError>
177    where
178        E: EntityValue,
179    {
180        Ok(self.execute()?.count())
181    }
182
183    /// Execute and require exactly one matching row.
184    pub fn require_one(&self) -> Result<(), QueryError>
185    where
186        E: EntityValue,
187    {
188        self.execute()?.require_one().map_err(QueryError::Response)
189    }
190
191    /// Execute and require at least one matching row.
192    pub fn require_some(&self) -> Result<(), QueryError>
193    where
194        E: EntityValue,
195    {
196        self.execute()?.require_some().map_err(QueryError::Response)
197    }
198}
199
200impl<C, E> SessionLoadQuery<'_, C, E>
201where
202    C: CanisterKind,
203    E: EntityKind<Canister = C> + SingletonEntity,
204    E::Key: Default,
205{
206    #[must_use]
207    pub fn only(mut self) -> Self {
208        self.query = self.query.only();
209        self
210    }
211}