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    key::Key,
13    traits::{CanisterKind, EntityKind, UnitKey},
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///
23pub struct SessionLoadQuery<'a, C: CanisterKind, E: EntityKind<Canister = C>> {
24    session: &'a DbSession<C>,
25    query: Query<E>,
26}
27
28impl<'a, C: CanisterKind, E: EntityKind<Canister = C>> SessionLoadQuery<'a, C, E> {
29    pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
30        Self { session, query }
31    }
32
33    // ------------------------------------------------------------------
34    // Intent inspection
35    // ------------------------------------------------------------------
36
37    #[must_use]
38    pub const fn query(&self) -> &Query<E> {
39        &self.query
40    }
41
42    // ------------------------------------------------------------------
43    // Intent builders (pure)
44    // ------------------------------------------------------------------
45
46    #[must_use]
47    pub fn by_key(mut self, key: impl Into<Key>) -> Self {
48        self.query = self.query.by_key(key.into());
49        self
50    }
51
52    #[must_use]
53    pub fn many<I>(mut self, keys: I) -> Self
54    where
55        I: IntoIterator<Item = E::PrimaryKey>,
56    {
57        self.query = self.query.by_keys(keys.into_iter().map(Into::into));
58        self
59    }
60
61    #[must_use]
62    pub fn filter(mut self, predicate: Predicate) -> Self {
63        self.query = self.query.filter(predicate);
64        self
65    }
66
67    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
68        self.query = self.query.filter_expr(expr)?;
69        Ok(self)
70    }
71
72    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
73        self.query = self.query.sort_expr(expr)?;
74        Ok(self)
75    }
76
77    #[must_use]
78    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
79        self.query = self.query.order_by(field);
80        self
81    }
82
83    #[must_use]
84    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
85        self.query = self.query.order_by_desc(field);
86        self
87    }
88
89    #[must_use]
90    pub fn limit(mut self, limit: u32) -> Self {
91        self.query = self.query.limit(limit);
92        self
93    }
94
95    #[must_use]
96    pub fn offset(mut self, offset: u32) -> Self {
97        self.query = self.query.offset(offset);
98        self
99    }
100
101    // ------------------------------------------------------------------
102    // Planning / diagnostics
103    // ------------------------------------------------------------------
104
105    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
106        self.query.explain()
107    }
108
109    pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
110        self.query.plan()
111    }
112
113    // ------------------------------------------------------------------
114    // Execution (single semantic boundary)
115    // ------------------------------------------------------------------
116
117    /// Execute this query using the session's policy settings.
118    pub fn execute(&self) -> Result<Response<E>, QueryError> {
119        self.session.execute_query(self.query())
120    }
121
122    // ------------------------------------------------------------------
123    // Execution terminals — semantic only
124    // ------------------------------------------------------------------
125
126    /// Execute and return whether the result set is empty.
127    pub fn is_empty(&self) -> Result<bool, QueryError> {
128        Ok(self.execute()?.is_empty())
129    }
130
131    /// Execute and return the number of matching rows.
132    pub fn count(&self) -> Result<u32, QueryError> {
133        Ok(self.execute()?.count())
134    }
135
136    /// Execute and require exactly one matching row.
137    pub fn require_one(&self) -> Result<(), QueryError> {
138        self.execute()?.require_one().map_err(QueryError::Response)
139    }
140
141    /// Execute and require at least one matching row.
142    pub fn require_some(&self) -> Result<(), QueryError> {
143        self.execute()?.require_some().map_err(QueryError::Response)
144    }
145}
146
147impl<C: CanisterKind, E: EntityKind<Canister = C>> SessionLoadQuery<'_, C, E>
148where
149    E::PrimaryKey: UnitKey,
150{
151    /// Load the singleton entity identified by the unit primary key `()`.
152    #[must_use]
153    pub fn only(mut self) -> Self {
154        self.query = self.query.only();
155        self
156    }
157}