rorm/crud/
delete.rs

1//! Delete builder and macro
2
3use std::marker::PhantomData;
4
5use rorm_db::database;
6use rorm_db::error::Error;
7use rorm_db::executor::Executor;
8
9use crate::conditions::{Condition, DynamicCollection};
10use crate::crud::selector::Selector;
11use crate::internal::patch::{IntoPatchCow, PatchCow};
12use crate::internal::query_context::QueryContext;
13use crate::model::{Identifiable, Model};
14use crate::Patch;
15
16/// Create a DELETE query.
17///
18/// # Usage
19/// ```no_run
20/// # use rorm::{Model, Patch, Database, delete};
21/// # #[derive(Model)] pub struct User { #[rorm(id)] id: i64, age: i32, }
22/// # #[derive(Patch)] #[rorm(model = "User")] pub struct UserPatch { id: i64, }
23/// pub async fn delete_single_user(db: &Database, user: &UserPatch) {
24///     delete(db, User)
25///         .single(user)
26///         .await
27///         .unwrap();
28/// }
29/// pub async fn delete_many_users(db: &Database, users: &[UserPatch]) {
30///     delete(db, User)
31///         .bulk(users)
32///         .await
33///         .unwrap();
34/// }
35/// pub async fn delete_underage(db: &Database) {
36///     let num_deleted: u64 = delete(db, User)
37///         .condition(User.age.less_equals(18))
38///         .await
39///         .unwrap();
40/// }
41///```
42///
43/// Like every crud macro `delete!` starts a [builder](DeleteBuilder) which is consumed to execute the query.
44///
45/// `delete!`'s first argument is a reference to the [`Database`](crate::Database).
46/// Its second is the [`Model`] type of whose table you want to delete columns from.
47///
48/// To specify what rows to delete use the following methods,
49/// which will consume the builder and execute the query:
50/// - [`single`](DeleteBuilder::single): Delete a single row identified by a patch instance
51/// - [`bulk`](DeleteBuilder::bulk): Delete a bulk of rows identified by patch instances
52/// - [`condition`](DeleteBuilder::condition): Delete all rows matching a condition
53/// - [`all`](DeleteBuilder::all): Unconditionally delete all rows
54pub fn delete<'ex, E, S>(executor: E, _: S) -> DeleteBuilder<E, S::Model>
55where
56    E: Executor<'ex>,
57    S: Selector<Model: Patch<ValueSpaceImpl = S>>,
58{
59    DeleteBuilder {
60        executor,
61
62        _phantom: PhantomData,
63    }
64}
65
66/// Builder for delete queries
67///
68/// To create a builder use [`delete`]
69///
70/// ## Generics
71/// - `E`: [`Executor`]
72///
73///     The executor to query with.
74///
75/// - `M`: [`Model`]
76///
77///     The model from whose table to delete rows.
78///
79#[must_use]
80pub struct DeleteBuilder<E, M> {
81    executor: E,
82
83    _phantom: PhantomData<M>,
84}
85
86impl<'ex, E, M> DeleteBuilder<E, M>
87where
88    E: Executor<'ex>,
89    M: Model,
90{
91    /// Delete a single row identified by a patch instance
92    ///
93    /// Note: The patch only provides the primary key, its other values will be ignored.
94    pub async fn single<P>(self, patch: &P) -> Result<u64, Error>
95    where
96        P: Patch<Model = M> + Identifiable,
97    {
98        self.condition(patch.as_condition()).await
99    }
100
101    /// Delete a bulk of rows identified by patch instances
102    ///
103    /// Note: The patches only provide the primary key, their other values will be ignored.
104    ///
105    /// # Argument
106    /// This method accepts anything which can be used to iterate
107    /// over instances or references of your [`Patch`].
108    ///
109    /// **Examples**: (where `P` is your patch)
110    /// - `Vec<P>`
111    /// - `&[P]`
112    /// - A [`map`](Iterator::map) iterator yielding `P` or `&P`
113    pub async fn bulk<'p, I, P>(self, patches: I) -> Result<u64, Error>
114    where
115        I: IntoIterator,
116        I::Item: IntoPatchCow<'p, Patch = P>,
117        P: Patch<Model = M> + Identifiable,
118    {
119        let mut owned = Vec::new();
120        let mut conditions = Vec::new();
121        for patch in patches {
122            match patch.into_patch_cow() {
123                PatchCow::Borrowed(patch) => conditions.push(patch.as_condition()),
124                PatchCow::Owned(patch) => owned.push(patch),
125            }
126        }
127        for patch in &owned {
128            conditions.push(patch.as_condition());
129        }
130        if let Some(condition) = DynamicCollection::or(conditions) {
131            self.condition(condition).await
132        } else {
133            Ok(0)
134        }
135    }
136
137    /// Delete all rows matching a condition
138    pub async fn condition<'c, C: Condition<'c>>(self, condition: C) -> Result<u64, Error> {
139        let mut context = QueryContext::new();
140        let condition_index = context.add_condition(&condition);
141        database::delete(
142            self.executor,
143            M::TABLE,
144            Some(&context.get_condition(condition_index)),
145        )
146        .await
147    }
148
149    /// Delete all rows
150    pub async fn all(self) -> Result<u64, Error> {
151        database::delete(self.executor, M::TABLE, None).await
152    }
153}