Skip to main content

drizzle_sqlite/values/
insert.rs

1//------------------------------------------------------------------------------
2// InsertValue Definition - SQL-based value for inserts
3//------------------------------------------------------------------------------
4
5use core::marker::PhantomData;
6
7use crate::prelude::*;
8use drizzle_core::{Placeholder, SQL, SQLParam, TypedPlaceholder};
9
10use super::{OwnedSQLiteValue, SQLiteValue};
11
12#[doc(hidden)]
13#[derive(Debug, Clone)]
14pub struct ValueWrapper<'a, V: SQLParam, T> {
15    pub value: SQL<'a, V>,
16    pub _phantom: PhantomData<T>,
17}
18
19impl<'a, V: SQLParam, T> ValueWrapper<'a, V, T> {
20    pub const fn new<U>(value: SQL<'a, V>) -> ValueWrapper<'a, V, U> {
21        ValueWrapper {
22            value,
23            _phantom: PhantomData,
24        }
25    }
26}
27
28/// One field of a generated insert model: left out, `NULL`, or a value.
29///
30/// You rarely build this directly. The `with_*` setters and `new` on a
31/// generated `InsertX` model convert their arguments into it.
32#[derive(Debug, Clone, Default)]
33#[allow(clippy::large_enum_variant)]
34pub enum SQLiteInsertValue<'a, V: SQLParam, T> {
35    /// Leave the column out of the INSERT so the database default applies.
36    #[default]
37    Omit,
38    /// Insert `NULL`.
39    Null,
40    /// Insert a bound value, placeholder or SQL expression.
41    Value(ValueWrapper<'a, V, T>),
42}
43
44impl<'a, T> SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
45    /// Converts this field to a `'static` form that owns its data.
46    ///
47    /// The whole SQL fragment is kept: placeholders stay unbound and
48    /// expressions such as `json(?)` keep their shape, with every bound value
49    /// detached from its borrow.
50    #[must_use]
51    pub fn into_owned(self) -> SQLiteInsertValue<'static, SQLiteValue<'static>, T> {
52        match self {
53            SQLiteInsertValue::Omit => SQLiteInsertValue::Omit,
54            SQLiteInsertValue::Null => SQLiteInsertValue::Null,
55            SQLiteInsertValue::Value(wrapper) => {
56                let static_sql = wrapper
57                    .value
58                    .into_owned_with(|value| SQLiteValue::from(OwnedSQLiteValue::from(value)));
59                SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'static>, T>::new(static_sql))
60            }
61        }
62    }
63}
64
65/// Converts a setter argument to the column's type, then to a bound value.
66///
67/// # Panics
68///
69/// Panics when the argument does not fit the column type (an integer out of
70/// range, a JSON payload that fails to serialize), rather than storing NULL in
71/// its place.
72impl<'a, T, U> From<T> for SQLiteInsertValue<'a, SQLiteValue<'a>, U>
73where
74    T: TryInto<SQLiteValue<'a>> + TryInto<U>,
75    U: TryInto<SQLiteValue<'a>>,
76{
77    fn from(value: T) -> Self {
78        // A value that does not fit the column type is a caller bug; storing
79        // NULL in its place would lose it silently.
80        let column_value = TryInto::<U>::try_into(value).unwrap_or_else(|_| {
81            panic!(
82                "a `{}` does not fit a `{}` column",
83                core::any::type_name::<T>(),
84                core::any::type_name::<U>()
85            )
86        });
87        let sql = SQL::from(
88            TryInto::<SQLiteValue<'a>>::try_into(column_value).unwrap_or_else(|_| {
89                panic!(
90                    "could not convert a `{}` to a SQLite value",
91                    core::any::type_name::<U>()
92                )
93            }),
94        );
95        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(sql))
96    }
97}
98
99impl<'a, T> From<Placeholder> for SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
100    fn from(placeholder: Placeholder) -> Self {
101        use drizzle_core::{Param, SQLChunk};
102        let chunk = SQLChunk::Param(Param {
103            placeholder,
104            value: None,
105        });
106        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(
107            core::iter::once(chunk).collect(),
108        ))
109    }
110}
111
112impl<'a, M: drizzle_core::types::DataType, N: drizzle_core::expr::Nullability, T>
113    From<TypedPlaceholder<M, N>> for SQLiteInsertValue<'a, SQLiteValue<'a>, T>
114{
115    fn from(typed: TypedPlaceholder<M, N>) -> Self {
116        Placeholder::from(typed).into()
117    }
118}
119
120// Array conversion for Vec<u8> InsertValue - support flexible input types
121impl<'a, const N: usize> From<[u8; N]> for SQLiteInsertValue<'a, SQLiteValue<'a>, Vec<u8>> {
122    fn from(value: [u8; N]) -> Self {
123        let sqlite_value = SQLiteValue::Blob(Cow::Owned(value.to_vec()));
124        let sql = SQL::param(sqlite_value);
125        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, Vec<u8>>::new(sql))
126    }
127}