use std::{
future::{Future, IntoFuture},
marker::PhantomData,
pin::Pin,
};
use tokio_postgres::types::ToSql;
use crate::TypedColumn;
use super::{push_all_with_sep, Executable, PushChunk, Query, SqlChunk, ToQuery, Where};
pub struct NoneSet;
pub struct SomeSet;
pub struct Update<'a, State = NoneSet> {
table: &'static str,
updates: Vec<SqlChunk<'a>>,
where_: Where<'a>,
state: PhantomData<State>,
}
impl<'a> ToQuery<'a, u64> for Update<'a, SomeSet> {}
impl<'a, T> Update<'a, T> {
pub fn new(table: &'static str) -> Update<'a, NoneSet> {
Update {
table,
updates: vec![],
where_: Where::Empty,
state: PhantomData::<NoneSet>,
}
}
pub fn where_(mut self, where_: Where<'a>) -> Update<'a, T> {
self.where_ = self.where_.and(where_);
self
}
pub fn set<U: ToSql + Sync>(
mut self,
col: TypedColumn<U>,
value: &'a U,
) -> Update<'a, SomeSet> {
self.updates
.push(SqlChunk(format!("{} = ?", col.column_name), vec![value]));
Update {
state: PhantomData::<SomeSet>,
updates: self.updates,
where_: self.where_,
table: self.table,
}
}
}
impl<'a> PushChunk<'a> for Update<'a, SomeSet> {
fn push_to_buffer<T>(&mut self, buffer: &mut super::Query<'a, T>) {
buffer.0.push_str("UPDATE ");
buffer.0.push_str(self.table);
buffer.0.push_str(" SET ");
push_all_with_sep(&mut self.updates, buffer, ", ");
if !self.where_.is_empty() {
buffer.0.push_str(" WHERE ");
self.where_.push_to_buffer(buffer);
}
}
}
impl<'a> IntoFuture for Update<'a, SomeSet>
where
Update<'a, SomeSet>: ToQuery<'a, u64>,
Query<'a, u64>: Executable<Output = u64>,
{
type Output = Result<u64, crate::Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + 'a>>;
fn into_future(mut self) -> Self::IntoFuture {
let query = self.to_query();
Box::pin(async move { query.exec().await })
}
}