use super::{OwnedPostgresValue, PostgresValue};
use drizzle_core::{
ToSQL, param::Param, placeholder::Placeholder, sql::SQL, sql::SQLChunk, traits::SQLParam,
};
use std::borrow::Cow;
use std::marker::PhantomData;
#[cfg(feature = "uuid")]
use uuid::Uuid;
#[derive(Debug, Clone)]
pub struct ValueWrapper<'a, V: SQLParam, T> {
pub value: SQL<'a, V>,
pub _phantom: PhantomData<T>,
}
impl<'a, V: SQLParam, T> ValueWrapper<'a, V, T> {
pub const fn new<U>(value: SQL<'a, V>) -> ValueWrapper<'a, V, U> {
ValueWrapper {
value,
_phantom: PhantomData,
}
}
}
#[derive(Debug, Clone, Default)]
#[allow(clippy::large_enum_variant)]
pub enum PostgresInsertValue<'a, V: SQLParam, T> {
#[default]
Omit,
Null,
Value(ValueWrapper<'a, V, T>),
}
impl<'a, T> PostgresInsertValue<'a, PostgresValue<'a>, T> {
pub fn into_owned(self) -> PostgresInsertValue<'static, PostgresValue<'static>, T> {
match self {
PostgresInsertValue::Omit => PostgresInsertValue::Omit,
PostgresInsertValue::Null => PostgresInsertValue::Null,
PostgresInsertValue::Value(wrapper) => {
if let Some(SQLChunk::Param(param)) = wrapper.value.chunks.first() {
if let Some(ref postgres_val) = param.value {
let postgres_val = postgres_val.as_ref();
let owned_postgres_val = OwnedPostgresValue::from(postgres_val.clone());
let static_postgres_val = PostgresValue::from(owned_postgres_val);
let static_sql = SQL::param(static_postgres_val);
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
static_sql,
))
} else {
let static_sql = SQL::param(PostgresValue::Null);
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
static_sql,
))
}
} else {
let static_sql = SQL::param(PostgresValue::Null);
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
static_sql,
))
}
}
}
}
}
impl<'a, T> From<T> for PostgresInsertValue<'a, PostgresValue<'a>, T>
where
T: TryInto<PostgresValue<'a>>,
{
fn from(value: T) -> Self {
let sql = value
.try_into()
.map(|v: PostgresValue<'a>| SQL::from(v))
.unwrap_or_else(|_| SQL::from(PostgresValue::Null));
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
}
}
impl<'a> From<&str> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
fn from(value: &str) -> Self {
let postgres_value = SQL::param(Cow::Owned(PostgresValue::from(value.to_string())));
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(
postgres_value,
))
}
}
impl<'a, T> From<Placeholder> for PostgresInsertValue<'a, PostgresValue<'a>, T> {
fn from(placeholder: Placeholder) -> Self {
let chunk = SQLChunk::Param(Param {
placeholder,
value: None,
});
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(
std::iter::once(chunk).collect(),
))
}
}
impl<'a, T> From<Option<T>> for PostgresInsertValue<'a, PostgresValue<'a>, T>
where
T: ToSQL<'a, PostgresValue<'a>>,
{
fn from(value: Option<T>) -> Self {
match value {
Some(v) => {
let sql = v.to_sql();
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
}
None => PostgresInsertValue::Omit,
}
}
}
#[cfg(feature = "uuid")]
impl<'a> From<Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
fn from(value: Uuid) -> Self {
let postgres_value = PostgresValue::Uuid(value);
let sql = SQL::param(postgres_value);
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
}
}
#[cfg(feature = "uuid")]
impl<'a> From<&'a Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
fn from(value: &'a Uuid) -> Self {
let postgres_value = PostgresValue::Uuid(*value);
let sql = SQL::param(postgres_value);
PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
}
}