Skip to main content

icydb_core/db/query/fluent/
delete.rs

1use crate::{
2    db::{
3        DbSession,
4        predicate::Predicate,
5        query::{
6            explain::ExplainPlan,
7            expr::{FilterExpr, SortExpr},
8            intent::{CompiledQuery, PlannedQuery, Query, QueryError},
9        },
10        response::Response,
11    },
12    traits::{EntityKind, EntityValue, SingletonEntity},
13    types::Id,
14};
15
16///
17/// FluentDeleteQuery
18///
19/// Session-bound delete query wrapper.
20/// This type owns *intent construction* and *execution routing only*.
21/// All result projection and cardinality handling lives on `Response<E>`.
22///
23
24pub struct FluentDeleteQuery<'a, E>
25where
26    E: EntityKind,
27{
28    session: &'a DbSession<E::Canister>,
29    query: Query<E>,
30}
31
32impl<'a, E> FluentDeleteQuery<'a, E>
33where
34    E: EntityKind,
35{
36    pub(crate) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
37        Self { session, query }
38    }
39
40    // ------------------------------------------------------------------
41    // Intent inspection
42    // ------------------------------------------------------------------
43
44    #[must_use]
45    pub const fn query(&self) -> &Query<E> {
46        &self.query
47    }
48
49    // ------------------------------------------------------------------
50    // Intent builders (pure)
51    // ------------------------------------------------------------------
52
53    /// Set the access path to a single typed primary-key value.
54    ///
55    /// `Id<E>` is treated as a plain query input value here. It does not grant access.
56    #[must_use]
57    pub fn by_id(mut self, id: Id<E>) -> Self {
58        self.query = self.query.by_id(id.key());
59        self
60    }
61
62    /// Set the access path to multiple typed primary-key values.
63    ///
64    /// IDs are public and may come from untrusted input sources.
65    #[must_use]
66    pub fn by_ids<I>(mut self, ids: I) -> Self
67    where
68        I: IntoIterator<Item = Id<E>>,
69    {
70        self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
71        self
72    }
73
74    // ------------------------------------------------------------------
75    // Query Refinement
76    // ------------------------------------------------------------------
77
78    #[must_use]
79    pub fn filter(mut self, predicate: Predicate) -> Self {
80        self.query = self.query.filter(predicate);
81        self
82    }
83
84    pub fn filter_expr(mut self, expr: FilterExpr) -> Result<Self, QueryError> {
85        self.query = self.query.filter_expr(expr)?;
86        Ok(self)
87    }
88
89    pub fn sort_expr(mut self, expr: SortExpr) -> Result<Self, QueryError> {
90        self.query = self.query.sort_expr(expr)?;
91        Ok(self)
92    }
93
94    #[must_use]
95    pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
96        self.query = self.query.order_by(field);
97        self
98    }
99
100    #[must_use]
101    pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
102        self.query = self.query.order_by_desc(field);
103        self
104    }
105
106    #[must_use]
107    pub fn limit(mut self, limit: u32) -> Self {
108        self.query = self.query.limit(limit);
109        self
110    }
111
112    // ------------------------------------------------------------------
113    // Planning / diagnostics
114    // ------------------------------------------------------------------
115
116    pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
117        self.query.explain()
118    }
119
120    pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
121        self.query.planned()
122    }
123
124    pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
125        self.query.plan()
126    }
127
128    // ------------------------------------------------------------------
129    // Execution (minimal core surface)
130    // ------------------------------------------------------------------
131
132    /// Execute this delete using the session's policy settings.
133    ///
134    /// All result inspection and projection is performed on `Response<E>`.
135    pub fn execute(&self) -> Result<Response<E>, QueryError>
136    where
137        E: EntityValue,
138    {
139        self.session.execute_query(self.query())
140    }
141
142    /// Execute and return whether any rows were affected.
143    pub fn is_empty(&self) -> Result<bool, QueryError>
144    where
145        E: EntityValue,
146    {
147        Ok(self.execute()?.is_empty())
148    }
149
150    /// Execute and return the number of affected rows.
151    pub fn count(&self) -> Result<u32, QueryError>
152    where
153        E: EntityValue,
154    {
155        Ok(self.execute()?.count())
156    }
157
158    /// Execute and require exactly one affected row.
159    pub fn require_one(&self) -> Result<(), QueryError>
160    where
161        E: EntityValue,
162    {
163        self.execute()?.require_one()?;
164        Ok(())
165    }
166
167    /// Execute and require at least one affected row.
168    pub fn require_some(&self) -> Result<(), QueryError>
169    where
170        E: EntityValue,
171    {
172        self.execute()?.require_some()?;
173        Ok(())
174    }
175}
176
177impl<E> FluentDeleteQuery<'_, E>
178where
179    E: EntityKind + SingletonEntity,
180    E::Key: Default,
181{
182    /// Delete the singleton entity.
183    #[must_use]
184    pub fn only(mut self) -> Self {
185        self.query = self.query.only();
186        self
187    }
188}