use crate::{
ActiveModelTrait, ActiveValue, ColumnTrait, DbBackend, DbErr, EntityTrait, IntoActiveModel,
Iterable, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryTrait,
query::column_tuple_in_condition,
sea_query::{IntoValueTuple, ValueTuple},
};
use core::marker::PhantomData;
use sea_query::DeleteStatement;
#[derive(Clone, Debug)]
pub struct Delete;
#[derive(Clone, Debug)]
pub struct DeleteOne<E: EntityTrait>(pub(crate) Result<ValidatedDeleteOne<E>, DbErr>);
#[derive(Clone, Debug)]
pub struct ValidatedDeleteOne<E: EntityTrait> {
pub(crate) query: DeleteStatement,
pub(crate) entity: PhantomData<E>,
}
impl<E: EntityTrait> TryFrom<DeleteOne<E>> for ValidatedDeleteOne<E> {
type Error = DbErr;
fn try_from(value: DeleteOne<E>) -> Result<Self, Self::Error> {
value.0
}
}
impl<E: EntityTrait> DeleteOne<E> {
pub fn validate(self) -> Result<ValidatedDeleteOne<E>, DbErr> {
self.try_into()
}
}
#[derive(Clone, Debug)]
pub struct DeleteMany<E>
where
E: EntityTrait,
{
pub(crate) query: DeleteStatement,
pub(crate) entity: PhantomData<E>,
}
impl Delete {
pub fn one<E, A, M>(model: M) -> DeleteOne<E>
where
E: EntityTrait,
A: ActiveModelTrait<Entity = E>,
M: IntoActiveModel<A>,
{
let model = model.into_active_model();
let mut myself = ValidatedDeleteOne {
query: DeleteStatement::new()
.from_table(A::Entity::default().table_ref())
.to_owned(),
entity: PhantomData,
};
for key in <A::Entity as EntityTrait>::PrimaryKey::iter() {
let col = key.into_column();
let av = model.get(col);
match av {
ActiveValue::Set(value) | ActiveValue::Unchanged(value) => {
myself = myself.filter(col.eq(value));
}
ActiveValue::NotSet => {
return DeleteOne(Err(DbErr::PrimaryKeyNotSet { ctx: "DeleteOne" }));
}
}
}
DeleteOne(Ok(myself))
}
#[doc(hidden)]
pub fn _one_only_for_use_by_model_ex<E: EntityTrait>(entity: E) -> ValidatedDeleteOne<E> {
ValidatedDeleteOne {
query: DeleteStatement::new()
.from_table(entity.table_ref())
.to_owned(),
entity: PhantomData,
}
}
pub fn many<E>(entity: E) -> DeleteMany<E>
where
E: EntityTrait,
{
DeleteMany {
query: DeleteStatement::new()
.from_table(entity.table_ref())
.to_owned(),
entity: PhantomData,
}
}
}
impl<E> QueryFilter for ValidatedDeleteOne<E>
where
E: EntityTrait,
{
type QueryStatement = DeleteStatement;
fn query(&mut self) -> &mut DeleteStatement {
&mut self.query
}
}
impl<E> QueryFilter for DeleteMany<E>
where
E: EntityTrait,
{
type QueryStatement = DeleteStatement;
fn query(&mut self) -> &mut DeleteStatement {
&mut self.query
}
}
impl<E> DeleteMany<E>
where
E: EntityTrait,
{
pub fn filter_by_ids<I>(mut self, values: I) -> Self
where
I: IntoIterator<Item = <E::PrimaryKey as PrimaryKeyTrait>::ValueType>,
{
self.query.cond_where(
column_tuple_in_condition(
&E::default().table_ref(),
&E::primary_key_identity(),
&values
.into_iter()
.map(|v| v.into_value_tuple())
.collect::<Vec<_>>(),
DbBackend::Sqlite,
)
.expect("trait bound ensured arity"),
);
self
}
#[doc(hidden)]
pub fn filter_by_value_tuples(mut self, values: &[ValueTuple], db_backend: DbBackend) -> Self {
self.query.cond_where(
column_tuple_in_condition(
&E::default().table_ref(),
&E::primary_key_identity(),
values,
db_backend,
)
.expect(""),
);
self
}
}
impl<E> QueryTrait for ValidatedDeleteOne<E>
where
E: EntityTrait,
{
type QueryStatement = DeleteStatement;
fn query(&mut self) -> &mut DeleteStatement {
&mut self.query
}
fn as_query(&self) -> &DeleteStatement {
&self.query
}
fn into_query(self) -> DeleteStatement {
self.query
}
}
impl<E> QueryTrait for DeleteMany<E>
where
E: EntityTrait,
{
type QueryStatement = DeleteStatement;
fn query(&mut self) -> &mut DeleteStatement {
&mut self.query
}
fn as_query(&self) -> &DeleteStatement {
&self.query
}
fn into_query(self) -> DeleteStatement {
self.query
}
}
#[cfg(test)]
mod tests {
use crate::tests_cfg::{cake, fruit};
use crate::{DbBackend, entity::*, query::*};
#[test]
fn delete_1() {
assert_eq!(
Delete::one(cake::Model {
id: 1,
name: "Apple Pie".to_owned(),
})
.validate()
.unwrap()
.build(DbBackend::Postgres)
.to_string(),
r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
);
assert_eq!(
Delete::one(cake::ActiveModel {
id: ActiveValue::set(1),
name: ActiveValue::set("Apple Pie".to_owned()),
})
.validate()
.unwrap()
.build(DbBackend::Postgres)
.to_string(),
r#"DELETE FROM "cake" WHERE "cake"."id" = 1"#,
);
}
#[test]
fn delete_2() {
assert_eq!(
Delete::many(fruit::Entity)
.filter(fruit::Column::Name.contains("Cheese"))
.build(DbBackend::Postgres)
.to_string(),
r#"DELETE FROM "fruit" WHERE "fruit"."name" LIKE '%Cheese%'"#,
);
}
}