use std::marker::PhantomData;
use rorm_db::database;
use rorm_db::error::Error;
use rorm_db::executor::Executor;
use crate::conditions::{Condition, DynamicCollection, Value};
use crate::crud::selector::Selector;
use crate::fields::proxy::{FieldProxy, FieldProxyImpl};
use crate::fields::utils::column_name::ColumnName;
use crate::internal::field::{Field, SingleColumnField};
use crate::internal::patch::{IntoPatchCow, PatchCow};
use crate::internal::query_context::QueryContext;
use crate::model::Identifiable;
use crate::{Model, Patch};
pub fn update<'rf, 'e, E, S>(executor: E, _: S) -> UpdateBuilder<'rf, E, S::Model, columns::Empty>
where
E: Executor<'e>,
S: Selector<Model: Patch<ValueSpaceImpl = S>>,
{
UpdateBuilder {
executor,
columns: Vec::new(),
_phantom: PhantomData,
}
}
#[must_use]
pub struct UpdateBuilder<'rf, E, M, C> {
executor: E,
columns: Vec<(&'static ColumnName, Value<'rf>)>,
_phantom: PhantomData<(M, C)>,
}
#[doc(hidden)]
pub mod columns {
pub struct Empty;
pub struct NonEmpty;
pub struct MaybeEmpty;
}
impl<'rf, E, M, C> UpdateBuilder<'rf, E, M, C> {
fn set_column_state<C2>(self) -> UpdateBuilder<'rf, E, M, C2> {
UpdateBuilder {
executor: self.executor,
columns: self.columns,
_phantom: PhantomData,
}
}
}
impl<'rf, E, M> UpdateBuilder<'rf, E, M, columns::Empty> {
pub fn begin_dyn_set(self) -> UpdateBuilder<'rf, E, M, columns::MaybeEmpty> {
self.set_column_state()
}
}
impl<'rf, E, M> UpdateBuilder<'rf, E, M, columns::MaybeEmpty> {
pub fn set<I>(mut self, _field: FieldProxy<I>, value: <I::Field as Field>::Type) -> Self
where
I: FieldProxyImpl<Field: SingleColumnField, Path = M>,
{
self.columns.push((
&<I::Field as Field>::NAME,
<I::Field as SingleColumnField>::type_into_value(value),
));
self
}
pub fn set_if<I>(self, field: FieldProxy<I>, value: Option<<I::Field as Field>::Type>) -> Self
where
I: FieldProxyImpl<Field: SingleColumnField, Path = M>,
{
if let Some(value) = value {
self.set(field, value)
} else {
self
}
}
pub fn finish_dyn_set(
self,
) -> Result<UpdateBuilder<'rf, E, M, columns::NonEmpty>, UpdateBuilder<'rf, E, M, columns::Empty>>
{
if self.columns.is_empty() {
Err(self.set_column_state())
} else {
Ok(self.set_column_state())
}
}
}
impl<'rf, E, M> UpdateBuilder<'rf, E, M, columns::Empty>
where
M: Model,
{
pub fn set<I>(
mut self,
_field: FieldProxy<I>,
value: <I::Field as Field>::Type,
) -> UpdateBuilder<'rf, E, M, columns::NonEmpty>
where
I: FieldProxyImpl<Field: SingleColumnField, Path = M>,
{
self.columns.push((
&<I::Field as Field>::NAME,
<I::Field as SingleColumnField>::type_into_value(value),
));
self.set_column_state()
}
}
impl<E, M> UpdateBuilder<'_, E, M, columns::NonEmpty>
where
M: Model,
{
pub fn set<I>(mut self, _field: FieldProxy<I>, value: <I::Field as Field>::Type) -> Self
where
I: FieldProxyImpl<Field: SingleColumnField, Path = M>,
{
self.columns.push((
&<I::Field as Field>::NAME,
<I::Field as SingleColumnField>::type_into_value(value),
));
self
}
pub fn set_if<I>(self, field: FieldProxy<I>, value: Option<<I::Field as Field>::Type>) -> Self
where
I: FieldProxyImpl<Field: SingleColumnField, Path = M>,
{
if let Some(value) = value {
self.set(field, value)
} else {
self
}
}
}
impl<'ex, 'rf, E, M> UpdateBuilder<'rf, E, M, columns::NonEmpty>
where
E: Executor<'ex>,
M: Model,
{
pub async fn single<P>(self, patch: &P) -> Result<u64, Error>
where
P: Patch<Model = M> + Identifiable,
{
self.condition(patch.as_condition()).await
}
pub async fn bulk<'p, I, P>(self, patches: I) -> Result<u64, Error>
where
I: IntoIterator,
I::Item: IntoPatchCow<'p, Patch = P>,
P: Patch<Model = M> + Identifiable,
{
let mut owned = Vec::new();
let mut conditions = Vec::new();
for patch in patches {
match patch.into_patch_cow() {
PatchCow::Borrowed(patch) => conditions.push(patch.as_condition()),
PatchCow::Owned(patch) => owned.push(patch),
}
}
for patch in &owned {
conditions.push(patch.as_condition());
}
if let Some(condition) = DynamicCollection::or(conditions) {
self.condition(condition).await
} else {
Ok(0)
}
}
pub async fn condition<C: Condition<'rf>>(self, condition: C) -> Result<u64, Error> {
let mut context = QueryContext::new();
let columns: Vec<_> = self
.columns
.iter()
.map(|(name, value)| (name.as_str(), value.as_sql()))
.collect();
let condition_index = context.add_condition(&condition);
let condition = context.get_condition(condition_index);
database::update(self.executor, M::TABLE, &columns, Some(&condition)).await
}
pub async fn all(self) -> Result<u64, Error> {
let columns: Vec<_> = self
.columns
.iter()
.map(|(name, value)| (name.as_str(), value.as_sql()))
.collect();
database::update(self.executor, M::TABLE, &columns, None).await
}
}