Skip to main content

drizzle_sqlite/values/
mod.rs

1//! `SQLite` value types and conversions
2//!
3//! This module contains the core `SQLiteValue` type and all its conversions.
4
5mod conversions;
6mod drivers;
7mod insert;
8pub mod owned;
9mod update;
10
11pub use insert::*;
12pub use owned::*;
13pub use update::*;
14
15use crate::prelude::*;
16use crate::traits::FromSQLiteValue;
17use drizzle_core::{dialect::Dialect, error::DrizzleError, sql::SQL, traits::SQLParam};
18
19//------------------------------------------------------------------------------
20// SQLiteValue Definition
21//------------------------------------------------------------------------------
22
23/// Represents a `SQLite` value
24#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
25pub enum SQLiteValue<'a> {
26    /// Integer value (i64)
27    Integer(i64),
28    /// Real value (f64)
29    Real(f64),
30    /// Text value (borrowed or owned string)
31    Text(Cow<'a, str>),
32    /// Blob value (borrowed or owned binary data)
33    Blob(Cow<'a, [u8]>),
34    /// NULL value
35    #[default]
36    Null,
37}
38
39/// Borrowed view of a `SQLite` value.
40///
41/// This is the zero-copy read-side representation used by custom column
42/// decoders. Text and blob payloads borrow directly from the driver row or
43/// from an existing [`SQLiteValue`].
44#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
45pub enum SQLiteValueRef<'a> {
46    /// Integer value (i64)
47    Integer(i64),
48    /// Real value (f64)
49    Real(f64),
50    /// Text value
51    Text(&'a str),
52    /// Blob value
53    Blob(&'a [u8]),
54    /// NULL value
55    #[default]
56    Null,
57}
58
59impl<'a> SQLiteValueRef<'a> {
60    /// Converts this borrowed value into a `SQLiteValue`.
61    #[inline]
62    #[must_use]
63    pub const fn into_value(self) -> SQLiteValue<'a> {
64        match self {
65            Self::Integer(value) => SQLiteValue::Integer(value),
66            Self::Real(value) => SQLiteValue::Real(value),
67            Self::Text(value) => SQLiteValue::Text(Cow::Borrowed(value)),
68            Self::Blob(value) => SQLiteValue::Blob(Cow::Borrowed(value)),
69            Self::Null => SQLiteValue::Null,
70        }
71    }
72
73    /// Converts a rusqlite borrowed value into a dialect-neutral borrowed
74    /// value.
75    ///
76    /// # Errors
77    ///
78    /// Returns [`DrizzleError::ConversionError`] if a `TEXT` value is not valid
79    /// UTF-8.
80    #[cfg(feature = "rusqlite")]
81    #[inline]
82    pub fn try_from_rusqlite_value_ref(
83        value: ::rusqlite::types::ValueRef<'a>,
84    ) -> Result<Self, DrizzleError> {
85        match value {
86            ::rusqlite::types::ValueRef::Null => Ok(Self::Null),
87            ::rusqlite::types::ValueRef::Integer(value) => Ok(Self::Integer(value)),
88            ::rusqlite::types::ValueRef::Real(value) => Ok(Self::Real(value)),
89            ::rusqlite::types::ValueRef::Text(value) => {
90                let value = core::str::from_utf8(value).map_err(|e| {
91                    DrizzleError::ConversionError(format!("invalid UTF-8: {e}").into())
92                })?;
93                Ok(Self::Text(value))
94            }
95            ::rusqlite::types::ValueRef::Blob(value) => Ok(Self::Blob(value)),
96        }
97    }
98}
99
100impl<'a> From<SQLiteValueRef<'a>> for SQLiteValue<'a> {
101    #[inline]
102    fn from(value: SQLiteValueRef<'a>) -> Self {
103        value.into_value()
104    }
105}
106
107impl<'a> From<&'a SQLiteValue<'_>> for SQLiteValueRef<'a> {
108    #[inline]
109    fn from(value: &'a SQLiteValue<'_>) -> Self {
110        value.as_ref()
111    }
112}
113
114impl SQLiteValue<'_> {
115    /// Returns true if this value is NULL.
116    #[inline]
117    #[must_use]
118    pub const fn is_null(&self) -> bool {
119        matches!(self, SQLiteValue::Null)
120    }
121
122    /// Returns the integer value if this is an INTEGER.
123    #[inline]
124    #[must_use]
125    pub const fn as_i64(&self) -> Option<i64> {
126        match self {
127            SQLiteValue::Integer(value) => Some(*value),
128            _ => None,
129        }
130    }
131
132    /// Returns the real value if this is a REAL.
133    #[inline]
134    #[must_use]
135    pub const fn as_f64(&self) -> Option<f64> {
136        match self {
137            SQLiteValue::Real(value) => Some(*value),
138            _ => None,
139        }
140    }
141
142    /// Returns the text value if this is TEXT.
143    #[inline]
144    #[must_use]
145    pub fn as_str(&self) -> Option<&str> {
146        match self {
147            SQLiteValue::Text(value) => Some(value.as_ref()),
148            _ => None,
149        }
150    }
151
152    /// Returns the blob value if this is BLOB.
153    #[inline]
154    #[must_use]
155    pub fn as_bytes(&self) -> Option<&[u8]> {
156        match self {
157            SQLiteValue::Blob(value) => Some(value.as_ref()),
158            _ => None,
159        }
160    }
161
162    /// Returns a borrowed view of this value.
163    #[inline]
164    #[must_use]
165    pub fn as_ref(&self) -> SQLiteValueRef<'_> {
166        match self {
167            SQLiteValue::Integer(value) => SQLiteValueRef::Integer(*value),
168            SQLiteValue::Real(value) => SQLiteValueRef::Real(*value),
169            SQLiteValue::Text(value) => SQLiteValueRef::Text(value.as_ref()),
170            SQLiteValue::Blob(value) => SQLiteValueRef::Blob(value.as_ref()),
171            SQLiteValue::Null => SQLiteValueRef::Null,
172        }
173    }
174
175    /// Converts this value into an owned representation.
176    #[inline]
177    #[must_use]
178    pub fn into_owned(self) -> OwnedSQLiteValue {
179        self.into()
180    }
181
182    /// Convert this `SQLite` value to a Rust type using the `FromSQLiteValue` trait.
183    ///
184    /// This provides a unified conversion interface for all types that implement
185    /// `FromSQLiteValue`, including primitives and enum types.
186    ///
187    /// # Errors
188    ///
189    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
190    /// be decoded into `T`.
191    ///
192    /// # Example
193    /// ```rust
194    /// # let _ = r####"
195    /// let value = SQLiteValue::Integer(42);
196    /// let num: i64 = value.convert()?;
197    /// # "####;
198    /// ```
199    pub fn convert<T: FromSQLiteValue>(self) -> Result<T, DrizzleError> {
200        T::from_sqlite_ref(self.as_ref())
201    }
202
203    /// Convert a reference to this `SQLite` value to a Rust type.
204    ///
205    /// # Errors
206    ///
207    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
208    /// be decoded into `T`.
209    pub fn convert_ref<T: FromSQLiteValue>(&self) -> Result<T, DrizzleError> {
210        T::from_sqlite_ref(self.as_ref())
211    }
212}
213
214impl core::fmt::Display for SQLiteValue<'_> {
215    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
216        let value = match self {
217            SQLiteValue::Integer(i) => i.to_string(),
218            SQLiteValue::Real(r) => r.to_string(),
219            SQLiteValue::Text(cow) => cow.to_string(),
220            SQLiteValue::Blob(cow) => String::from_utf8_lossy(cow).to_string(),
221            SQLiteValue::Null => String::new(),
222        };
223        write!(f, "{value}")
224    }
225}
226
227// Implement core traits required by Drizzle
228impl SQLParam for SQLiteValue<'_> {
229    const DIALECT: Dialect = Dialect::SQLite;
230    type DialectMarker = drizzle_core::dialect::SQLiteDialect;
231}
232
233impl<'a> From<SQLiteValue<'a>> for SQL<'a, SQLiteValue<'a>> {
234    fn from(value: SQLiteValue<'a>) -> Self {
235        SQL::param(value)
236    }
237}
238
239impl FromIterator<OwnedSQLiteValue> for Vec<SQLiteValue<'_>> {
240    fn from_iter<T: IntoIterator<Item = OwnedSQLiteValue>>(iter: T) -> Self {
241        iter.into_iter().map(SQLiteValue::from).collect()
242    }
243}
244
245impl<'a> FromIterator<&'a OwnedSQLiteValue> for Vec<SQLiteValue<'a>> {
246    fn from_iter<T: IntoIterator<Item = &'a OwnedSQLiteValue>>(iter: T) -> Self {
247        iter.into_iter().map(SQLiteValue::from).collect()
248    }
249}