Skip to main content

drizzle_sqlite/values/
mod.rs

1//! `SQLite` values: [`SQLiteValue`] (bound parameters and decoded cells),
2//! its owned and borrowed forms, and the insert and update field types used
3//! by generated models.
4
5mod conversions;
6mod drivers;
7#[cfg(any(feature = "chrono", feature = "time"))]
8pub(crate) mod duration;
9mod insert;
10#[cfg(feature = "serde")]
11mod json;
12pub mod owned;
13mod update;
14
15pub use insert::*;
16pub use owned::*;
17pub use update::*;
18
19use crate::prelude::*;
20use crate::traits::FromSQLiteValue;
21use drizzle_core::{dialect::Dialect, error::DrizzleError, sql::SQL, traits::SQLParam};
22
23//------------------------------------------------------------------------------
24// SQLiteValue Definition
25//------------------------------------------------------------------------------
26
27/// A value in one of `SQLite`'s five storage classes.
28///
29/// Used for bound parameters and for values read from a row. Text and blob
30/// payloads may borrow for `'a`; see [`OwnedSQLiteValue`] for an owned
31/// form. Booleans are stored as `Integer(0)` / `Integer(1)`.
32#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
33pub enum SQLiteValue<'a> {
34    /// Integer value (i64)
35    Integer(i64),
36    /// Real value (f64)
37    Real(f64),
38    /// Text value (borrowed or owned string)
39    Text(Cow<'a, str>),
40    /// Blob value (borrowed or owned binary data)
41    Blob(Cow<'a, [u8]>),
42    /// NULL value
43    #[default]
44    Null,
45}
46
47/// Borrowed view of a `SQLite` value.
48///
49/// This is the zero-copy read-side representation used by custom column
50/// decoders. Text and blob payloads borrow directly from the driver row or
51/// from an existing [`SQLiteValue`].
52#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
53pub enum SQLiteValueRef<'a> {
54    /// Integer value (i64)
55    Integer(i64),
56    /// Real value (f64)
57    Real(f64),
58    /// Text value
59    Text(&'a str),
60    /// Blob value
61    Blob(&'a [u8]),
62    /// NULL value
63    #[default]
64    Null,
65}
66
67impl<'a> SQLiteValueRef<'a> {
68    /// Converts this borrowed value into a `SQLiteValue`.
69    #[inline]
70    #[must_use]
71    pub const fn into_value(self) -> SQLiteValue<'a> {
72        match self {
73            Self::Integer(value) => SQLiteValue::Integer(value),
74            Self::Real(value) => SQLiteValue::Real(value),
75            Self::Text(value) => SQLiteValue::Text(Cow::Borrowed(value)),
76            Self::Blob(value) => SQLiteValue::Blob(Cow::Borrowed(value)),
77            Self::Null => SQLiteValue::Null,
78        }
79    }
80
81    /// Converts a rusqlite borrowed value into a dialect-neutral borrowed
82    /// value.
83    ///
84    /// # Errors
85    ///
86    /// Returns [`DrizzleError::ConversionError`] if a `TEXT` value is not valid
87    /// UTF-8.
88    #[cfg(feature = "rusqlite")]
89    #[inline]
90    pub fn try_from_rusqlite_value_ref(
91        value: ::rusqlite::types::ValueRef<'a>,
92    ) -> Result<Self, DrizzleError> {
93        match value {
94            ::rusqlite::types::ValueRef::Null => Ok(Self::Null),
95            ::rusqlite::types::ValueRef::Integer(value) => Ok(Self::Integer(value)),
96            ::rusqlite::types::ValueRef::Real(value) => Ok(Self::Real(value)),
97            ::rusqlite::types::ValueRef::Text(value) => {
98                let value = core::str::from_utf8(value).map_err(|e| {
99                    DrizzleError::ConversionError(format!("invalid UTF-8: {e}").into())
100                })?;
101                Ok(Self::Text(value))
102            }
103            ::rusqlite::types::ValueRef::Blob(value) => Ok(Self::Blob(value)),
104        }
105    }
106}
107
108impl<'a> From<SQLiteValueRef<'a>> for SQLiteValue<'a> {
109    #[inline]
110    fn from(value: SQLiteValueRef<'a>) -> Self {
111        value.into_value()
112    }
113}
114
115impl<'a> From<&'a SQLiteValue<'_>> for SQLiteValueRef<'a> {
116    #[inline]
117    fn from(value: &'a SQLiteValue<'_>) -> Self {
118        value.as_ref()
119    }
120}
121
122impl SQLiteValue<'_> {
123    /// Returns true if this value is NULL.
124    #[inline]
125    #[must_use]
126    pub const fn is_null(&self) -> bool {
127        matches!(self, SQLiteValue::Null)
128    }
129
130    /// Returns the integer value if this is an INTEGER.
131    #[inline]
132    #[must_use]
133    pub const fn as_i64(&self) -> Option<i64> {
134        match self {
135            SQLiteValue::Integer(value) => Some(*value),
136            _ => None,
137        }
138    }
139
140    /// Returns the real value if this is a REAL.
141    #[inline]
142    #[must_use]
143    pub const fn as_f64(&self) -> Option<f64> {
144        match self {
145            SQLiteValue::Real(value) => Some(*value),
146            _ => None,
147        }
148    }
149
150    /// Returns the text value if this is TEXT.
151    #[inline]
152    #[must_use]
153    pub fn as_str(&self) -> Option<&str> {
154        match self {
155            SQLiteValue::Text(value) => Some(value.as_ref()),
156            _ => None,
157        }
158    }
159
160    /// Returns the blob value if this is BLOB.
161    #[inline]
162    #[must_use]
163    pub fn as_bytes(&self) -> Option<&[u8]> {
164        match self {
165            SQLiteValue::Blob(value) => Some(value.as_ref()),
166            _ => None,
167        }
168    }
169
170    /// Returns a borrowed view of this value.
171    #[inline]
172    #[must_use]
173    pub fn as_ref(&self) -> SQLiteValueRef<'_> {
174        match self {
175            SQLiteValue::Integer(value) => SQLiteValueRef::Integer(*value),
176            SQLiteValue::Real(value) => SQLiteValueRef::Real(*value),
177            SQLiteValue::Text(value) => SQLiteValueRef::Text(value.as_ref()),
178            SQLiteValue::Blob(value) => SQLiteValueRef::Blob(value.as_ref()),
179            SQLiteValue::Null => SQLiteValueRef::Null,
180        }
181    }
182
183    /// Converts this value into an owned representation.
184    #[inline]
185    #[must_use]
186    pub fn into_owned(self) -> OwnedSQLiteValue {
187        self.into()
188    }
189
190    /// Decodes this value into `T` with [`FromSQLiteValue`].
191    ///
192    /// # Examples
193    ///
194    /// ```rust
195    /// # use drizzle_sqlite::values::SQLiteValue;
196    /// let num: i64 = SQLiteValue::Integer(42).convert()?;
197    /// assert_eq!(num, 42);
198    /// # Ok::<(), drizzle_core::error::DrizzleError>(())
199    /// ```
200    ///
201    /// # Errors
202    ///
203    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
204    /// be decoded into `T`.
205    pub fn convert<T: FromSQLiteValue>(self) -> Result<T, DrizzleError> {
206        T::from_sqlite_ref(self.as_ref())
207    }
208
209    /// Decodes this value into `T` without consuming it. See
210    /// [`convert`](Self::convert).
211    ///
212    /// # Errors
213    ///
214    /// Returns [`DrizzleError::ConversionError`] when the stored variant cannot
215    /// be decoded into `T`.
216    pub fn convert_ref<T: FromSQLiteValue>(&self) -> Result<T, DrizzleError> {
217        T::from_sqlite_ref(self.as_ref())
218    }
219}
220
221impl core::fmt::Display for SQLiteValue<'_> {
222    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
223        let value = match self {
224            SQLiteValue::Integer(i) => i.to_string(),
225            SQLiteValue::Real(r) => r.to_string(),
226            SQLiteValue::Text(cow) => cow.to_string(),
227            SQLiteValue::Blob(cow) => String::from_utf8_lossy(cow).to_string(),
228            SQLiteValue::Null => String::new(),
229        };
230        write!(f, "{value}")
231    }
232}
233
234// Implement core traits required by Drizzle
235impl SQLParam for SQLiteValue<'_> {
236    const DIALECT: Dialect = Dialect::SQLite;
237    type DialectMarker = drizzle_core::dialect::SQLiteDialect;
238
239    fn write_literal(&self, buf: &mut String) -> bool {
240        use core::fmt::Write;
241        match self {
242            SQLiteValue::Null => buf.push_str("NULL"),
243            SQLiteValue::Integer(value) => {
244                let _ = write!(buf, "{value}");
245            }
246            // `{:?}` keeps a decimal point or exponent, so SQLite reads a
247            // REAL rather than an INTEGER. SQLite has no NaN literal, and
248            // stores NaN as NULL anyway.
249            SQLiteValue::Real(value) if value.is_nan() => return false,
250            SQLiteValue::Real(value) if value.is_infinite() => {
251                buf.push_str(if value.is_sign_positive() {
252                    "9e999"
253                } else {
254                    "-9e999"
255                });
256            }
257            SQLiteValue::Real(value) => {
258                let _ = write!(buf, "{value:?}");
259            }
260            SQLiteValue::Text(text) if text.contains('\0') => return false,
261            SQLiteValue::Text(text) => {
262                buf.push('\'');
263                buf.push_str(&text.replace('\'', "''"));
264                buf.push('\'');
265            }
266            SQLiteValue::Blob(bytes) => {
267                buf.push_str("X'");
268                for byte in bytes.iter() {
269                    let _ = write!(buf, "{byte:02X}");
270                }
271                buf.push('\'');
272            }
273        }
274        true
275    }
276}
277
278impl<'a> From<SQLiteValue<'a>> for SQL<'a, SQLiteValue<'a>> {
279    fn from(value: SQLiteValue<'a>) -> Self {
280        SQL::param(value)
281    }
282}
283
284impl FromIterator<OwnedSQLiteValue> for Vec<SQLiteValue<'_>> {
285    fn from_iter<T: IntoIterator<Item = OwnedSQLiteValue>>(iter: T) -> Self {
286        iter.into_iter().map(SQLiteValue::from).collect()
287    }
288}
289
290impl<'a> FromIterator<&'a OwnedSQLiteValue> for Vec<SQLiteValue<'a>> {
291    fn from_iter<T: IntoIterator<Item = &'a OwnedSQLiteValue>>(iter: T) -> Self {
292        iter.into_iter().map(SQLiteValue::from).collect()
293    }
294}