#![warn(missing_docs)]
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
#[cfg(feature = "mysql")]
use sqlx::MySqlPool;
#[cfg(feature = "postgres")]
use sqlx::PgPool;
#[cfg(feature = "sqlite")]
use sqlx::SqlitePool;
use crate::filter::Filtered;
use crate::helpers::{StartingSql, bind_value, build_filter_expr, get_starting_sql};
use crate::schema::{UpdateTrait, Value};
use crate::{database::error::DatabaseError, schema::Schema};
#[derive(Debug)]
pub struct Update<T: Schema + Debug, U: UpdateTrait + Debug> {
table: PhantomData<T>,
update_table: PhantomData<U>,
filters: Vec<Box<dyn Filtered>>,
#[cfg(feature = "mysql")]
conn: Arc<MySqlPool>,
#[cfg(feature = "postgres")]
conn: Arc<PgPool>,
#[cfg(feature = "sqlite")]
conn: Arc<SqlitePool>,
update_data: Vec<(&'static str, Value)>,
}
impl<T: Schema + Debug, U: UpdateTrait + Debug> Update<T, U> {
#[cfg(feature = "mysql")]
pub(crate) fn new(conn: Arc<MySqlPool>) -> Self {
Self {
table: PhantomData,
update_table: PhantomData,
filters: Vec::new(),
update_data: Vec::new(),
conn,
}
}
#[cfg(feature = "postgres")]
pub(crate) fn new(conn: Arc<PgPool>) -> Self {
Self {
table: PhantomData,
update_table: PhantomData,
filters: Vec::new(),
update_data: Vec::new(),
conn,
}
}
#[cfg(feature = "sqlite")]
pub(crate) fn new(conn: Arc<SqlitePool>) -> Self {
Self {
table: PhantomData,
update_table: PhantomData,
filters: Vec::new(),
update_data: Vec::new(),
conn,
}
}
pub fn set(mut self, data: U) -> Self {
self.update_data = data.get_updated();
self
}
pub fn filter<F>(mut self, filter: F) -> Self
where
F: Filtered + 'static,
{
self.filters.push(Box::new(filter));
self
}
pub async fn execute(self) -> Result<(), DatabaseError> {
let sql = get_starting_sql(StartingSql::Update, T::table_name());
let sql = Self::update_sql(sql, self.update_data);
let mut params: Vec<Value> = Vec::new();
let sql = Self::filter_sql(sql, self.filters, &mut params);
let mut conn = self
.conn
.acquire()
.await
.map_err(DatabaseError::ConnectionError)?;
let mut query = sqlx::query(&sql);
for v in params {
query = bind_value(query, v);
}
query
.execute(conn.as_mut())
.await
.map_err(|e| DatabaseError::ExecutionError(e.to_string()))?;
Ok(())
}
pub(crate) fn update_sql(mut sql: String, data: Vec<(&'static str, Value)>) -> String {
if data.is_empty() {
return sql;
}
for column in data {
sql.push_str(&format!("{} = {}", column.0, column.1));
sql.push_str(" ");
}
sql
}
pub(crate) fn filter_sql(
mut sql: String,
filters: Vec<Box<dyn Filtered>>,
params: &mut Vec<Value>,
) -> String {
if filters.is_empty() {
return sql;
}
sql.push_str(" WHERE ");
let mut parts: Vec<String> = Vec::with_capacity(filters.len());
for filter in &filters {
parts.push(build_filter_expr(filter.as_ref(), params));
}
sql.push_str(&parts.join(" AND "));
sql
}
}