drizzle-sqlite 0.2.1

A type-safe SQL query builder for Rust
Documentation
//------------------------------------------------------------------------------
// InsertValue Definition - SQL-based value for inserts
//------------------------------------------------------------------------------

use core::marker::PhantomData;

use crate::prelude::*;
use drizzle_core::{Placeholder, SQL, SQLParam, TypedPlaceholder};

use super::{OwnedSQLiteValue, SQLiteValue};

#[doc(hidden)]
#[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,
        }
    }
}

/// Represents a value for INSERT operations that can be omitted, null, or a SQL expression
#[derive(Debug, Clone, Default)]
#[allow(clippy::large_enum_variant)]
pub enum SQLiteInsertValue<'a, V: SQLParam, T> {
    /// Omit this column from the INSERT (use database default)
    #[default]
    Omit,
    /// Explicitly insert NULL
    Null,
    /// Insert a SQL expression (value, placeholder, etc.)
    Value(ValueWrapper<'a, V, T>),
}

impl<'a, T> SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
    /// Converts this `InsertValue` to an owned version with 'static lifetime.
    ///
    /// The whole SQL fragment is kept: placeholders stay unbound and
    /// expressions such as `json(?)` keep their shape, with every bound value
    /// detached from its borrow.
    #[must_use]
    pub fn into_owned(self) -> SQLiteInsertValue<'static, SQLiteValue<'static>, T> {
        match self {
            SQLiteInsertValue::Omit => SQLiteInsertValue::Omit,
            SQLiteInsertValue::Null => SQLiteInsertValue::Null,
            SQLiteInsertValue::Value(wrapper) => {
                let static_sql = wrapper
                    .value
                    .into_owned_with(|value| SQLiteValue::from(OwnedSQLiteValue::from(value)));
                SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'static>, T>::new(static_sql))
            }
        }
    }
}

/// Converts a setter argument to the column's type, then to a bound value.
///
/// # Panics
///
/// Panics when the argument does not fit the column type (an integer out of
/// range, a JSON payload that fails to serialize), rather than storing NULL in
/// its place.
impl<'a, T, U> From<T> for SQLiteInsertValue<'a, SQLiteValue<'a>, U>
where
    T: TryInto<SQLiteValue<'a>> + TryInto<U>,
    U: TryInto<SQLiteValue<'a>>,
{
    fn from(value: T) -> Self {
        // A value that does not fit the column type is a caller bug; storing
        // NULL in its place would lose it silently.
        let column_value = TryInto::<U>::try_into(value).unwrap_or_else(|_| {
            panic!(
                "a `{}` does not fit a `{}` column",
                core::any::type_name::<T>(),
                core::any::type_name::<U>()
            )
        });
        let sql = SQL::from(
            TryInto::<SQLiteValue<'a>>::try_into(column_value).unwrap_or_else(|_| {
                panic!(
                    "could not convert a `{}` to a SQLite value",
                    core::any::type_name::<U>()
                )
            }),
        );
        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(sql))
    }
}

impl<'a, T> From<Placeholder> for SQLiteInsertValue<'a, SQLiteValue<'a>, T> {
    fn from(placeholder: Placeholder) -> Self {
        use drizzle_core::{Param, SQLChunk};
        let chunk = SQLChunk::Param(Param {
            placeholder,
            value: None,
        });
        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, T>::new(
            core::iter::once(chunk).collect(),
        ))
    }
}

impl<'a, M: drizzle_core::types::DataType, N: drizzle_core::expr::Nullability, T>
    From<TypedPlaceholder<M, N>> for SQLiteInsertValue<'a, SQLiteValue<'a>, T>
{
    fn from(typed: TypedPlaceholder<M, N>) -> Self {
        Placeholder::from(typed).into()
    }
}

// Array conversion for Vec<u8> InsertValue - support flexible input types
impl<'a, const N: usize> From<[u8; N]> for SQLiteInsertValue<'a, SQLiteValue<'a>, Vec<u8>> {
    fn from(value: [u8; N]) -> Self {
        let sqlite_value = SQLiteValue::Blob(Cow::Owned(value.to_vec()));
        let sql = SQL::param(sqlite_value);
        SQLiteInsertValue::Value(ValueWrapper::<SQLiteValue<'a>, Vec<u8>>::new(sql))
    }
}