use std::future::Future;
#[derive(Debug, Clone, PartialEq)]
#[derive(Default)]
pub enum UpdateField<T> {
#[default]
Unchanged,
Set(T),
}
impl<T> UpdateField<T> {
pub fn is_set(&self) -> bool {
matches!(self, UpdateField::Set(_))
}
pub fn is_unchanged(&self) -> bool {
matches!(self, UpdateField::Unchanged)
}
}
pub trait ToSqlParam: Send + Sync {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync);
}
pub trait Row: Send {
fn get_i16(&self, idx: usize) -> i16;
fn get_i32(&self, idx: usize) -> i32;
fn get_i64(&self, idx: usize) -> i64;
fn get_f32(&self, idx: usize) -> f32;
fn get_f64(&self, idx: usize) -> f64;
fn get_string(&self, idx: usize) -> String;
fn get_bool(&self, idx: usize) -> bool;
fn get_uuid(&self, idx: usize) -> uuid::Uuid;
fn get_timestamp(&self, idx: usize) -> chrono::NaiveDateTime;
fn get_timestamptz(&self, idx: usize) -> chrono::DateTime<chrono::Utc>;
fn get_date(&self, idx: usize) -> chrono::NaiveDate;
fn get_decimal(&self, idx: usize) -> rust_decimal::Decimal;
fn get_json(&self, idx: usize) -> serde_json::Value;
fn get_opt_i16(&self, idx: usize) -> Option<i16>;
fn get_opt_i32(&self, idx: usize) -> Option<i32>;
fn get_opt_i64(&self, idx: usize) -> Option<i64>;
fn get_opt_f32(&self, idx: usize) -> Option<f32>;
fn get_opt_f64(&self, idx: usize) -> Option<f64>;
fn get_opt_string(&self, idx: usize) -> Option<String>;
fn get_opt_bool(&self, idx: usize) -> Option<bool>;
fn get_opt_uuid(&self, idx: usize) -> Option<uuid::Uuid>;
fn get_opt_timestamp(&self, idx: usize) -> Option<chrono::NaiveDateTime>;
fn get_opt_timestamptz(&self, idx: usize) -> Option<chrono::DateTime<chrono::Utc>>;
fn get_opt_date(&self, idx: usize) -> Option<chrono::NaiveDate>;
fn get_opt_decimal(&self, idx: usize) -> Option<rust_decimal::Decimal>;
fn get_opt_json(&self, idx: usize) -> Option<serde_json::Value>;
fn get_vec_i16(&self, idx: usize) -> Vec<i16>;
fn get_vec_i32(&self, idx: usize) -> Vec<i32>;
fn get_vec_i64(&self, idx: usize) -> Vec<i64>;
fn get_vec_f32(&self, idx: usize) -> Vec<f32>;
fn get_vec_f64(&self, idx: usize) -> Vec<f64>;
fn get_vec_string(&self, idx: usize) -> Vec<String>;
fn get_vec_bool(&self, idx: usize) -> Vec<bool>;
fn get_vec_uuid(&self, idx: usize) -> Vec<uuid::Uuid>;
}
pub trait QueryExecutor: Send + Sync {
type Error: std::error::Error + Send + Sync + 'static;
type Row: Row;
fn query(
&self,
sql: &str,
params: &[&dyn ToSqlParam],
) -> impl Future<Output = Result<Vec<Self::Row>, Self::Error>> + Send;
fn execute(
&self,
sql: &str,
params: &[&dyn ToSqlParam],
) -> impl Future<Output = Result<u64, Self::Error>> + Send;
}
pub trait Client: QueryExecutor {
type Transaction<'a>: Transaction<Error = Self::Error, Row = Self::Row> + 'a where Self: 'a;
fn transaction(&mut self) -> impl Future<Output = Result<Self::Transaction<'_>, Self::Error>> + Send;
}
pub trait Transaction: QueryExecutor {
type Nested<'a>: Transaction<Error = Self::Error, Row = Self::Row> + 'a where Self: 'a;
fn transaction(&mut self) -> impl Future<Output = Result<Self::Nested<'_>, Self::Error>> + Send;
fn commit(self) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn rollback(self) -> impl Future<Output = Result<(), Self::Error>> + Send;
}
macro_rules! impl_to_sql_param {
($($ty:ty),*) => {
$(
impl ToSqlParam for $ty {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
)*
};
}
impl_to_sql_param!(
i16, i32, i64, f32, f64, bool, String,
uuid::Uuid, chrono::NaiveDateTime, chrono::NaiveDate,
rust_decimal::Decimal
);
impl ToSqlParam for chrono::DateTime<chrono::Utc> {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
impl ToSqlParam for serde_json::Value {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
impl ToSqlParam for &'static str {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
impl<T: ToSqlParam + 'static> ToSqlParam for Option<T> {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
impl<T: ToSqlParam + 'static> ToSqlParam for Vec<T> {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
self
}
}
impl<T: ToSqlParam + 'static> ToSqlParam for UpdateField<T> {
fn as_any(&self) -> &(dyn std::any::Any + Send + Sync) {
match self {
UpdateField::Set(v) => v.as_any(),
UpdateField::Unchanged => panic!("Unchanged fields should not be passed as SQL params"),
}
}
}
#[cfg(feature = "backend-tokio-postgres")]
pub mod tokio_postgres_impl;