Skip to main content

drizzle_postgres/values/
insert.rs

1//! [`PostgresInsertValue`], the field type of generated insert models.
2
3use super::{OwnedPostgresValue, PostgresValue};
4use crate::prelude::*;
5use core::marker::PhantomData;
6use drizzle_core::{
7    ToSQL, TypedPlaceholder, param::Param, placeholder::Placeholder, sql::SQL, sql::SQLChunk,
8    traits::SQLParam,
9};
10
11#[cfg(feature = "uuid")]
12use uuid::Uuid;
13
14//------------------------------------------------------------------------------
15// InsertValue Definition - SQL-based value for inserts
16//------------------------------------------------------------------------------
17
18#[doc(hidden)]
19#[derive(Debug, Clone)]
20pub struct ValueWrapper<'a, V: SQLParam, T> {
21    pub value: SQL<'a, V>,
22    pub _phantom: PhantomData<T>,
23}
24
25impl<'a, V: SQLParam, T> ValueWrapper<'a, V, T> {
26    pub const fn new<U>(value: SQL<'a, V>) -> ValueWrapper<'a, V, U> {
27        ValueWrapper {
28            value,
29            _phantom: PhantomData,
30        }
31    }
32}
33
34/// One column of a row in an `INSERT`: omitted, `NULL`, or a value or SQL expression.
35///
36/// Generated `Insert*` models store their fields as this type. `T` is the
37/// column's Rust type, used to check what can be assigned. Converting an
38/// `Option` maps `None` to [`Omit`](Self::Omit), not to `NULL`.
39#[derive(Debug, Clone, Default)]
40#[allow(clippy::large_enum_variant)]
41pub enum PostgresInsertValue<'a, V: SQLParam, T> {
42    /// Leave the column out, so the database default applies.
43    #[default]
44    Omit,
45    /// Insert `NULL`.
46    Null,
47    /// Insert a bound value, placeholder or SQL expression.
48    Value(ValueWrapper<'a, V, T>),
49}
50
51impl<'a, T> PostgresInsertValue<'a, PostgresValue<'a>, T> {
52    /// Converts to a `'static` value that owns all its data.
53    ///
54    /// The whole SQL fragment is kept: placeholders stay unbound and
55    /// expressions keep their shape, with every bound value detached from its
56    /// borrow.
57    #[must_use]
58    pub fn into_owned(self) -> PostgresInsertValue<'static, PostgresValue<'static>, T> {
59        match self {
60            PostgresInsertValue::Omit => PostgresInsertValue::Omit,
61            PostgresInsertValue::Null => PostgresInsertValue::Null,
62            PostgresInsertValue::Value(wrapper) => {
63                let static_sql = wrapper
64                    .value
65                    .into_owned_with(|value| PostgresValue::from(OwnedPostgresValue::from(value)));
66                PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'static>, T>::new(
67                    static_sql,
68                ))
69            }
70        }
71    }
72}
73
74// Conversion implementations for PostgresValue-based InsertValue
75
76/// Converts any value that converts to a [`PostgresValue`] (enums,
77/// `ArrayString`, `ArrayVec`, ...).
78///
79/// # Panics
80///
81/// Panics when the value fails to convert (a JSON payload that fails to
82/// serialize, for example), rather than storing NULL in its place.
83impl<'a, T> From<T> for PostgresInsertValue<'a, PostgresValue<'a>, T>
84where
85    T: TryInto<PostgresValue<'a>>,
86{
87    fn from(value: T) -> Self {
88        // A failed conversion is a caller bug; storing NULL in its place would
89        // lose the value silently.
90        let sql = SQL::from(
91            TryInto::<PostgresValue<'a>>::try_into(value).unwrap_or_else(|_| {
92                panic!(
93                    "could not convert a `{}` to a PostgreSQL value",
94                    core::any::type_name::<T>()
95                )
96            }),
97        );
98        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(sql))
99    }
100}
101
102// Specific conversion for &str to String InsertValue
103impl<'a> From<&str> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
104    fn from(value: &str) -> Self {
105        let postgres_value = SQL::param(Cow::Owned(PostgresValue::from(value.to_string())));
106        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(
107            postgres_value,
108        ))
109    }
110}
111
112// Placeholder conversion
113impl<'a, T> From<Placeholder> for PostgresInsertValue<'a, PostgresValue<'a>, T> {
114    fn from(placeholder: Placeholder) -> Self {
115        let chunk = SQLChunk::Param(Param {
116            placeholder,
117            value: None,
118        });
119        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(
120            core::iter::once(chunk).collect(),
121        ))
122    }
123}
124
125impl<'a, M: drizzle_core::types::DataType, N: drizzle_core::expr::Nullability, T>
126    From<TypedPlaceholder<M, N>> for PostgresInsertValue<'a, PostgresValue<'a>, T>
127{
128    fn from(typed: TypedPlaceholder<M, N>) -> Self {
129        Placeholder::from(typed).into()
130    }
131}
132
133// Option conversion
134impl<'a, T> From<Option<T>> for PostgresInsertValue<'a, PostgresValue<'a>, T>
135where
136    T: ToSQL<'a, PostgresValue<'a>>,
137{
138    fn from(value: Option<T>) -> Self {
139        value.map_or(PostgresInsertValue::Omit, |v| {
140            PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, T>::new(v.to_sql()))
141        })
142    }
143}
144
145// UUID conversion for String InsertValue (for text columns)
146#[cfg(feature = "uuid")]
147impl<'a> From<Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
148    fn from(value: Uuid) -> Self {
149        let postgres_value = PostgresValue::Uuid(value);
150        let sql = SQL::param(postgres_value);
151        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
152    }
153}
154
155#[cfg(feature = "uuid")]
156impl<'a> From<&'a Uuid> for PostgresInsertValue<'a, PostgresValue<'a>, String> {
157    fn from(value: &'a Uuid) -> Self {
158        let postgres_value = PostgresValue::Uuid(*value);
159        let sql = SQL::param(postgres_value);
160        PostgresInsertValue::Value(ValueWrapper::<PostgresValue<'a>, String>::new(sql))
161    }
162}