use std::fmt;
use keelson_core::Mod;
use keelson_exec::{ExecError, ExecResult, Execute as _, Executor, FromRow, Row};
use crate::Table;
use crate::delegate::delegate_clause;
fn decode_rows<T: FromRow>(rows: Vec<Row>) -> Result<Vec<T>, ExecError> {
rows.into_iter().map(|mut r| T::from_row(&mut r)).collect()
}
pub struct ModelInsert<M: Table> {
setter: M::Setter,
#[allow(clippy::type_complexity)] mods: Vec<Box<dyn FnOnce(&mut M::Insert) + Send>>,
}
impl<M: Table> fmt::Debug for ModelInsert<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModelInsert")
.field("mods", &self.mods.len())
.finish_non_exhaustive()
}
}
impl<M: Table> ModelInsert<M> {
pub(crate) fn new(setter: M::Setter) -> Self {
ModelInsert {
setter,
mods: Vec::new(),
}
}
#[must_use]
pub fn with(mut self, mods: impl Mod<M::Insert> + Send + 'static) -> Self {
self.mods.push(Box::new(move |q| mods.apply(q)));
self
}
async fn build(self, db: &dyn Executor) -> Result<M::Insert, ExecError> {
let ModelInsert { mut setter, mods } = self;
M::before_insert(db, &mut setter).await?;
let mut q = M::insert_query(setter);
for m in mods {
m(&mut q);
}
Ok(q)
}
pub async fn one(self, db: &dyn Executor) -> Result<M::Row, ExecError> {
let q = self.build(db).await?;
let mut models: Vec<M::Row> = decode_rows(q.fetch_rows(db).await?)?;
match models.len() {
0 => Err(ExecError::RowNotFound),
1 => {
M::after_insert(db, &models).await?;
Ok(models.pop().expect("len checked"))
}
_ => Err(ExecError::TooManyRows),
}
}
pub async fn exec(self, db: &dyn Executor) -> Result<ExecResult, ExecError> {
let q = self.build(db).await?;
let done = q.execute(db).await?;
M::after_insert(db, &[]).await?;
Ok(done)
}
}
pub struct ModelUpdate<M: Table> {
setter: M::Setter,
query: M::Update,
}
impl<M: Table> fmt::Debug for ModelUpdate<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModelUpdate")
.field("query", &self.query)
.finish_non_exhaustive()
}
}
impl<M: Table> ModelUpdate<M> {
pub(crate) fn new(setter: M::Setter, mods: impl Mod<Self>) -> Self {
let mut u = ModelUpdate {
setter,
query: M::update_query(),
};
mods.apply(&mut u);
u
}
pub fn apply(&mut self, mods: impl Mod<M::Update>) {
mods.apply(&mut self.query);
}
async fn build(self, db: &dyn Executor) -> Result<M::Update, ExecError> {
let ModelUpdate {
mut setter,
mut query,
} = self;
M::before_update(db, &mut setter).await?;
M::apply_setter(setter, &mut query);
Ok(query)
}
pub async fn exec(self, db: &dyn Executor) -> Result<ExecResult, ExecError> {
let q = self.build(db).await?;
let done = q.execute(db).await?;
M::after_update(db, done.rows_affected).await?;
Ok(done)
}
pub async fn all(self, db: &dyn Executor) -> Result<Vec<M::Row>, ExecError> {
let q = self.build(db).await?;
let models: Vec<M::Row> = decode_rows(q.fetch_rows(db).await?)?;
M::after_update(db, models.len() as u64).await?;
Ok(models)
}
}
pub struct ModelDelete<M: Table> {
query: M::Delete,
}
impl<M: Table> fmt::Debug for ModelDelete<M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModelDelete")
.field("query", &self.query)
.finish()
}
}
impl<M: Table> ModelDelete<M> {
pub(crate) fn new(mods: impl Mod<Self>) -> Self {
let mut d = ModelDelete {
query: M::delete_query(),
};
mods.apply(&mut d);
d
}
pub fn apply(&mut self, mods: impl Mod<M::Delete>) {
mods.apply(&mut self.query);
}
pub async fn exec(self, db: &dyn Executor) -> Result<ExecResult, ExecError> {
M::before_delete(db).await?;
let done = self.query.execute(db).await?;
M::after_delete(db, done.rows_affected).await?;
Ok(done)
}
pub async fn all(self, db: &dyn Executor) -> Result<Vec<M::Row>, ExecError> {
M::before_delete(db).await?;
let models: Vec<M::Row> = decode_rows(self.query.fetch_rows(db).await?)?;
M::after_delete(db, models.len() as u64).await?;
Ok(models)
}
}
delegate_clause!(
ModelUpdate,
Table,
Update,
HasWith,
with_mut,
keelson_core::clause::With
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasTableRef,
table_ref_mut,
keelson_core::clause::TableRef
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasJoins,
joins_mut,
Vec<keelson_core::clause::Join>
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasWhere,
where_mut,
keelson_core::clause::Where
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasOrderBy,
order_by_mut,
keelson_core::clause::OrderBy
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasLimit,
limit_mut,
keelson_core::clause::Limit
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasSet,
set_mut,
keelson_core::clause::Set
);
delegate_clause!(
ModelUpdate,
Table,
Update,
HasReturning,
returning_mut,
keelson_core::clause::Returning
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasWith,
with_mut,
keelson_core::clause::With
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasTableRef,
table_ref_mut,
keelson_core::clause::TableRef
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasJoins,
joins_mut,
Vec<keelson_core::clause::Join>
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasWhere,
where_mut,
keelson_core::clause::Where
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasOrderBy,
order_by_mut,
keelson_core::clause::OrderBy
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasLimit,
limit_mut,
keelson_core::clause::Limit
);
delegate_clause!(
ModelDelete,
Table,
Delete,
HasReturning,
returning_mut,
keelson_core::clause::Returning
);