use std::marker::PhantomData;
use field_access::FieldAccess;
use sqlx::{Database, Encode, Error, QueryBuilder, Type};
use crate::common::{
conversion::ValueConvert, error::QueryError, fields::extract_with_bind, filter::push_primary_key_conditions, helper::get_table_name, types::PrimaryKey
};
pub struct Update<'a, ET, DB, VAL>
where
DB: Database,
{
query_builder: QueryBuilder<'a, DB>,
_phantom: PhantomData<(ET, VAL)>,
}
impl<'a, ET, DB, VAL> Update<'a, ET, DB, VAL>
where
ET: FieldAccess,
DB: Database,
VAL: Encode<'a, DB> + Type<DB> + 'a,
{
pub fn table() -> Self {
Self::with_table(get_table_name::<ET>())
}
pub fn with_table(table_name: impl Into<String>) -> Self {
Self::from_query_with_table(QueryBuilder::new(""), &table_name.into())
}
pub fn from_query(qb: QueryBuilder<'a, DB>) -> Self {
Self::from_query_with_table(qb, &get_table_name::<ET>())
}
pub fn from_query_with_table(mut query_builder: QueryBuilder<'a, DB>, table_name: impl Into<String>) -> Self {
query_builder.push("UPDATE ")
.push(table_name.into()).push(" SET ");
Self {
query_builder,
_phantom: PhantomData,
}
}
pub fn one(
model: &'a ET,
primary_key: &PrimaryKey<'a>,
skip_non_null: bool,
) -> Result<QueryBuilder<'a, DB>, Error>
where
VAL: Encode<'a, DB> + Type<DB> + ValueConvert + Default + 'a,
{
let keys = primary_key.clone();
let filter_keys = if primary_key.auto_generate() {
primary_key.get_keys()
} else {
vec![]
};
let mut query_builder = Self::table().query_builder;
let mut first = true;
let fields = extract_with_bind::<VAL, _>(
model.fields(),
&filter_keys,
skip_non_null,
|name, value| {
if !first {
query_builder.push(", ");
}
first = false;
query_builder.push(format!("{} = ", name)).push_bind(value);
},
);
if fields.0.is_empty() {
return Err(QueryError::ColumnsListEmpty.into());
}
query_builder.push(" WHERE ");
push_primary_key_conditions::<ET, DB, VAL>(&mut query_builder, model, &keys);
Ok(query_builder)
}
pub fn custom(
mut self,
build_fn: impl FnOnce(&mut QueryBuilder<'a, DB>)
) -> Self
{
build_fn(&mut self.query_builder);
self
}
pub fn filter(
mut self,
filter_build_fn: impl FnOnce(&mut QueryBuilder<'a, DB>),
) -> Self {
self.query_builder.push(" WHERE ");
filter_build_fn(&mut self.query_builder);
self
}
#[cfg(any(feature = "sqlite" , feature = "postgres"))]
pub fn returning<I, S>(mut self, columns: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.query_builder.push(" RETURNING ");
let cols: Vec<String> = columns.into_iter().map(|s| s.as_ref().to_string()).collect();
let mut separated = self.query_builder.separated(", ");
for col in cols {
separated.push(col);
}
self
}
pub fn finish(self) -> QueryBuilder<'a, DB> {
self.query_builder
}
}