Skip to main content

drizzle_sqlite/traits/
value.rs

1//! Value conversion traits for `SQLite` types
2//!
3//! This module provides the `FromSQLiteValue` trait for converting `SQLite` values
4//! to Rust types, and row capability traits for unified access across drivers.
5
6use crate::prelude::*;
7use crate::values::{OwnedSQLiteValue, SQLiteValue, SQLiteValueRef};
8use drizzle_core::conv::checked_float_to_int;
9use drizzle_core::error::DrizzleError;
10
11/// SQLite affinity metadata owned by a Drizzle SQL type marker.
12pub trait SQLiteAffinity: drizzle_core::types::DataType {
13    /// SQLite affinity spelling used in DDL and schema metadata.
14    const SQL_TYPE: &'static str;
15}
16
17macro_rules! impl_sqlite_affinity {
18    ($($marker:ty => $sql_type:literal),+ $(,)?) => {
19        $(
20            impl SQLiteAffinity for $marker {
21                const SQL_TYPE: &'static str = $sql_type;
22            }
23        )+
24    };
25}
26
27impl_sqlite_affinity! {
28    crate::types::Integer => "INTEGER",
29    crate::types::Text => "TEXT",
30    crate::types::Blob => "BLOB",
31    crate::types::Real => "REAL",
32    crate::types::Numeric => "NUMERIC",
33    crate::types::Any => "ANY",
34}
35
36/// Trait for custom Rust types that map to a `SQLite` column.
37///
38/// Generated by `#[derive(SQLiteEnum)]`. Implement this trait manually for
39/// custom wrappers that should be usable directly in table schemas.
40///
41/// # Associated Types
42///
43/// - `SQLType`: The Drizzle SQL type marker used for typed expressions and,
44///   through [`SQLiteAffinity`], DDL/schema affinity metadata.
45///
46/// # Required Methods
47///
48/// - `decode`: Convert a borrowed `SQLite` value read from the database into `Self`
49/// - `encode`: Convert self to a `SQLiteValue` for insertion/updates
50///
51/// The blanket `From<Self> for SQLiteValue` owns the encoded value because
52/// insert/update models may store SQL fragments after the source value is
53/// dropped. Call `encode()` directly when you need an immediate borrowed value.
54/// Override `encode_owned()` when consuming `self` can avoid cloning owned data.
55#[diagnostic::on_unimplemented(
56    message = "`{Self}` cannot be used as a SQLite column type",
57    note = "add #[derive(SQLiteEnum)] for enum types, or use a supported primitive type"
58)]
59pub trait DrizzleSQLiteColumn: Sized {
60    /// Drizzle SQL type marker for this column.
61    ///
62    /// Use one of the built-in SQLite markers, such as `Text`, `Integer`,
63    /// `Blob`, `Real`, `Numeric`, or `Any`.
64    type SQLType: SQLiteAffinity;
65
66    /// SQLite affinity spelling retained for source compatibility with custom
67    /// column implementations.
68    ///
69    /// Schema generation uses [`SQLType`](Self::SQLType) and
70    /// [`SQLiteAffinity`] as the authority, so overriding this constant does
71    /// not change the generated column type.
72    const SQL_TYPE: &'static str = <Self::SQLType as SQLiteAffinity>::SQL_TYPE;
73
74    /// Decode a borrowed `SQLite` value read from the database into `Self`.
75    ///
76    /// Implementations should reject unsupported storage classes with
77    /// [`DrizzleError::ConversionError`].
78    ///
79    /// # Errors
80    ///
81    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be decoded
82    /// as this custom column type.
83    fn decode(value: SQLiteValueRef<'_>) -> Result<Self, DrizzleError>;
84
85    /// Convert self to a `SQLiteValue` for insertion/updates.
86    fn encode(&self) -> SQLiteValue<'_>;
87
88    /// Convert self to an owned `SQLite` value for stored bind parameters.
89    ///
90    /// The default implementation owns the borrowed result of [`encode`](Self::encode).
91    /// Override this for wrappers that can move an internal string or byte buffer
92    /// directly into the SQL parameter.
93    fn encode_owned(self) -> OwnedSQLiteValue {
94        self.encode().into_owned()
95    }
96
97    /// Decode a value emitted by SQLite's storage-class-preserving JSON query
98    /// projection.
99    ///
100    /// # Errors
101    ///
102    /// Returns [`DrizzleError::ConversionError`] when the JSON value cannot be
103    /// represented by this column's SQLite storage codec.
104    #[cfg(feature = "query")]
105    fn decode_json(value: &serde_json::Value) -> Result<Self, DrizzleError> {
106        let value = decode_projected_sqlite_value(value)?;
107        let value = value.as_value();
108        Self::decode(value.as_ref())
109    }
110}
111
112/// Decode the tagged value emitted for a storage-codec candidate in SQLite's
113/// JSON query projection.
114///
115/// The tag carries `typeof(column)` alongside the value, so a TEXT cell in a
116/// BLOB-affinity column remains TEXT and is rejected by codecs that require
117/// BLOB instead of being silently interpreted as hexadecimal bytes.
118///
119/// # Errors
120///
121/// Returns [`DrizzleError::ConversionError`] for malformed tags, values that do
122/// not match their tagged SQLite storage class, or invalid BLOB hex.
123#[cfg(feature = "query")]
124pub fn decode_projected_sqlite_value(
125    value: &serde_json::Value,
126) -> Result<OwnedSQLiteValue, DrizzleError> {
127    if value.is_null() {
128        return Ok(OwnedSQLiteValue::Null);
129    }
130
131    let object = value.as_object().ok_or_else(|| {
132        DrizzleError::ConversionError("SQLite query value is missing its storage tag".into())
133    })?;
134    let storage = object
135        .get("$drizzle_storage")
136        .and_then(serde_json::Value::as_str)
137        .ok_or_else(|| {
138            DrizzleError::ConversionError("SQLite query value has no valid $drizzle_storage".into())
139        })?;
140    let value = object.get("$drizzle_value").ok_or_else(|| {
141        DrizzleError::ConversionError("SQLite query value has no $drizzle_value".into())
142    })?;
143
144    match storage {
145        "integer" => value
146            .as_i64()
147            .map(OwnedSQLiteValue::Integer)
148            .ok_or_else(|| {
149                DrizzleError::ConversionError(
150                    "SQLite INTEGER query value is not a JSON integer".into(),
151                )
152            }),
153        "real" => value.as_f64().map(OwnedSQLiteValue::Real).ok_or_else(|| {
154            DrizzleError::ConversionError("SQLite REAL query value is not a JSON number".into())
155        }),
156        "text" => value
157            .as_str()
158            .map(|value| OwnedSQLiteValue::Text(value.to_owned()))
159            .ok_or_else(|| {
160                DrizzleError::ConversionError("SQLite TEXT query value is not a JSON string".into())
161            }),
162        "blob" => value
163            .as_str()
164            .ok_or_else(|| {
165                DrizzleError::ConversionError("SQLite BLOB query value is not a hex string".into())
166            })
167            .and_then(decode_projected_blob)
168            .map(|value| OwnedSQLiteValue::Blob(value.into_boxed_slice())),
169        other => Err(DrizzleError::ConversionError(
170            format!("unknown SQLite query storage class `{other}`").into(),
171        )),
172    }
173}
174
175#[cfg(feature = "query")]
176fn decode_projected_blob(value: &str) -> Result<Vec<u8>, DrizzleError> {
177    let (chunks, remainder) = value.as_bytes().as_chunks::<2>();
178    let mut bytes = Vec::with_capacity(value.len() / 2);
179    for chunk in chunks {
180        let high = decode_hex_nibble(chunk[0])?;
181        let low = decode_hex_nibble(chunk[1])?;
182        bytes.push((high << 4) | low);
183    }
184    if !remainder.is_empty() {
185        return Err(DrizzleError::ConversionError(
186            "hex-encoded SQLite BLOB has odd length".into(),
187        ));
188    }
189    Ok(bytes)
190}
191
192#[cfg(feature = "query")]
193fn decode_hex_nibble(value: u8) -> Result<u8, DrizzleError> {
194    match value {
195        b'0'..=b'9' => Ok(value - b'0'),
196        b'a'..=b'f' => Ok(value - b'a' + 10),
197        b'A'..=b'F' => Ok(value - b'A' + 10),
198        _ => Err(DrizzleError::ConversionError(
199            "SQLite BLOB query value contains a non-hex byte".into(),
200        )),
201    }
202}
203
204impl<'a, T> From<T> for SQLiteValue<'a>
205where
206    T: DrizzleSQLiteColumn,
207{
208    fn from(value: T) -> Self {
209        value.encode_owned().into()
210    }
211}
212
213/// Trait for types that can be converted from `SQLite` values.
214///
215/// `SQLite` has 5 storage classes: NULL, INTEGER, REAL, TEXT, BLOB.
216/// This trait provides conversion methods for each type.
217///
218/// # Implementation Notes
219///
220/// - Implement the methods that make sense for your type
221/// - Return `Err` for unsupported conversions
222/// - `SQLiteEnum` derive automatically implements this trait
223pub trait FromSQLiteValue: Sized {
224    /// Convert from a 64-bit integer value.
225    ///
226    /// # Errors
227    ///
228    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be represented as `Self`.
229    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError>;
230
231    /// Convert from a text/string value.
232    ///
233    /// # Errors
234    ///
235    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be parsed or represented as `Self`.
236    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError>;
237
238    /// Convert from a real/float value.
239    ///
240    /// # Errors
241    ///
242    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be represented as `Self`.
243    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError>;
244
245    /// Convert from a blob/binary value.
246    ///
247    /// # Errors
248    ///
249    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be interpreted as `Self`.
250    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError>;
251
252    /// Convert from a borrowed SQLite storage value.
253    ///
254    /// # Errors
255    ///
256    /// Returns [`DrizzleError::ConversionError`] if `value` cannot be
257    /// represented as `Self`.
258    fn from_sqlite_ref(value: SQLiteValueRef<'_>) -> Result<Self, DrizzleError> {
259        match value {
260            SQLiteValueRef::Integer(value) => Self::from_sqlite_integer(value),
261            SQLiteValueRef::Text(value) => Self::from_sqlite_text(value),
262            SQLiteValueRef::Real(value) => Self::from_sqlite_real(value),
263            SQLiteValueRef::Blob(value) => Self::from_sqlite_blob(value),
264            SQLiteValueRef::Null => Self::from_sqlite_null(),
265        }
266    }
267
268    /// Convert from a NULL value (default returns error).
269    ///
270    /// # Errors
271    ///
272    /// The default implementation always returns [`DrizzleError::ConversionError`];
273    /// override for nullable-aware types (e.g. [`Option`]).
274    fn from_sqlite_null() -> Result<Self, DrizzleError> {
275        Err(DrizzleError::ConversionError(
276            "unexpected NULL value".into(),
277        ))
278    }
279
280    /// Helper function to convert from rusqlite's `ValueRef` using `FromSQLiteValue`.
281    ///
282    /// # Errors
283    ///
284    /// Returns [`DrizzleError::ConversionError`] if the underlying conversion fails,
285    /// including UTF-8 decoding failures for TEXT values.
286    #[cfg(feature = "rusqlite")]
287    fn from_value_ref(value: ::rusqlite::types::ValueRef<'_>) -> Result<Self, DrizzleError> {
288        let value = SQLiteValueRef::try_from_rusqlite_value_ref(value)?;
289        Self::from_sqlite_ref(value)
290    }
291}
292
293impl<T> FromSQLiteValue for T
294where
295    T: DrizzleSQLiteColumn,
296{
297    fn from_sqlite_ref(value: SQLiteValueRef<'_>) -> Result<Self, DrizzleError> {
298        T::decode(value)
299    }
300
301    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
302        T::decode(SQLiteValueRef::Integer(value))
303    }
304
305    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
306        T::decode(SQLiteValueRef::Text(value))
307    }
308
309    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
310        T::decode(SQLiteValueRef::Real(value))
311    }
312
313    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
314        T::decode(SQLiteValueRef::Blob(value))
315    }
316
317    fn from_sqlite_null() -> Result<Self, DrizzleError> {
318        T::decode(SQLiteValueRef::Null)
319    }
320}
321
322/// Row capability for index-based extraction.
323pub trait DrizzleRowByIndex {
324    /// Get a column value by index.
325    ///
326    /// # Errors
327    ///
328    /// Returns [`DrizzleError`] if the index is out of range or the column value
329    /// cannot be converted to `T`.
330    fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError>;
331}
332
333/// Optional row capability for name-based extraction.
334pub trait DrizzleRowByName: DrizzleRowByIndex {
335    /// Get a column value by name.
336    ///
337    /// # Errors
338    ///
339    /// Returns [`DrizzleError`] if the name does not resolve to a column or the
340    /// column value cannot be converted to `T`.
341    fn get_column_by_name<T: FromSQLiteValue>(&self, name: &str) -> Result<T, DrizzleError>;
342}
343
344// =============================================================================
345// Primitive implementations
346// =============================================================================
347
348/// Macro to implement `FromSQLiteValue` for integer types (handles narrowing conversion from i64)
349macro_rules! impl_from_sqlite_value_int {
350    // Special case for i64 - no conversion needed
351    (i64) => {
352        impl FromSQLiteValue for i64 {
353            fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
354                Ok(value)
355            }
356
357            fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
358                value.parse().map_err(|e| {
359                    DrizzleError::ConversionError(format!("cannot parse '{}' as i64: {}", value, e).into())
360                })
361            }
362
363            fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
364                checked_float_to_int(value, "i64")
365            }
366
367            fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
368                Err(DrizzleError::ConversionError("cannot convert BLOB to i64".into()))
369            }
370        }
371    };
372    // General case for other integer types - uses try_into for narrowing
373    ($($ty:ty),+ $(,)?) => {
374        $(
375            impl FromSQLiteValue for $ty {
376                fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
377                    value.try_into().map_err(|e| {
378                        DrizzleError::ConversionError(
379                            format!("i64 {} out of range for {}: {}", value, stringify!($ty), e).into(),
380                        )
381                    })
382                }
383
384                fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
385                    value.parse().map_err(|e| {
386                        DrizzleError::ConversionError(
387                            format!("cannot parse '{}' as {}: {}", value, stringify!($ty), e).into()
388                        )
389                    })
390                }
391
392                fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
393                    checked_float_to_int(value, stringify!($ty))
394                }
395
396                fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
397                    Err(DrizzleError::ConversionError(
398                        concat!("cannot convert BLOB to ", stringify!($ty)).into()
399                    ))
400                }
401            }
402        )+
403    };
404}
405
406/// IEEE 754 `f64` representation of an `i64` built from two exact halves via
407/// [`From`], matching the semantics of `i as f64` without an `as`-cast.
408#[inline]
409fn i64_to_f64(i: i64) -> f64 {
410    let high = i32::try_from(i >> 32).expect("sign-extended high word fits in i32");
411    let low = u32::try_from(i & 0xFFFF_FFFF).expect("masked low word fits in u32");
412    f64::from(high) * 4_294_967_296.0_f64 + f64::from(low)
413}
414
415// Integer types
416impl_from_sqlite_value_int!(i64);
417impl_from_sqlite_value_int!(i8, i16, i32, isize, u8, u16, u32, u64, usize);
418
419// f64 — `i64` widening via split-halves is exact in the mantissa range and
420// matches direct `as` cast beyond it; no cast is needed for f64→f64.
421impl FromSQLiteValue for f64 {
422    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
423        Ok(i64_to_f64(value))
424    }
425
426    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
427        value.parse().map_err(|e| {
428            DrizzleError::ConversionError(format!("cannot parse '{value}' as f64: {e}").into())
429        })
430    }
431
432    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
433        Ok(value)
434    }
435
436    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
437        Err(DrizzleError::ConversionError(
438            "cannot convert BLOB to f64".into(),
439        ))
440    }
441}
442
443fn narrow_to_f32(value: f64) -> Result<f32, DrizzleError> {
444    // Round-trip through a decimal string so IEEE 754 round-to-nearest is
445    // applied explicitly (and clippy doesn't flag the conversion as a lossy
446    // numeric cast).
447    format!("{value}").parse::<f32>().map_err(|e| {
448        DrizzleError::ConversionError(format!("cannot convert REAL {value} to f32: {e}").into())
449    })
450}
451
452// f32 — narrowing from both `i64` and `f64` is intentionally lossy; SQLite
453// round-trips the value through `f64` storage so we defer to the standard
454// IEEE 754 narrowing conversion. These casts are the only correct semantics.
455impl FromSQLiteValue for f32 {
456    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
457        // Go via f64 (exact) then narrow to f32.
458        narrow_to_f32(i64_to_f64(value))
459    }
460
461    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
462        value.parse().map_err(|e| {
463            DrizzleError::ConversionError(format!("cannot parse '{value}' as f32: {e}").into())
464        })
465    }
466
467    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
468        narrow_to_f32(value)
469    }
470
471    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
472        Err(DrizzleError::ConversionError(
473            "cannot convert BLOB to f32".into(),
474        ))
475    }
476}
477
478impl FromSQLiteValue for bool {
479    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
480        Ok(value != 0)
481    }
482
483    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
484        match value.to_lowercase().as_str() {
485            "true" | "1" | "yes" | "on" => Ok(true),
486            "false" | "0" | "no" | "off" => Ok(false),
487            _ => Err(DrizzleError::ConversionError(
488                format!("cannot parse '{value}' as bool").into(),
489            )),
490        }
491    }
492
493    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
494        Ok(value != 0.0)
495    }
496
497    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
498        Err(DrizzleError::ConversionError(
499            "cannot convert BLOB to bool".into(),
500        ))
501    }
502}
503
504impl FromSQLiteValue for String {
505    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
506        Ok(value.to_string())
507    }
508
509    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
510        Ok(value.to_string())
511    }
512
513    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
514        Ok(value.to_string())
515    }
516
517    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
518        Self::from_utf8(value.to_vec()).map_err(|e| {
519            DrizzleError::ConversionError(format!("invalid UTF-8 in BLOB: {e}").into())
520        })
521    }
522}
523
524impl FromSQLiteValue for compact_str::CompactString {
525    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
526        Ok(Self::new(value.to_string()))
527    }
528
529    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
530        Ok(Self::new(value))
531    }
532
533    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
534        Ok(Self::new(value.to_string()))
535    }
536
537    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
538        let s = String::from_utf8(value.to_vec()).map_err(|e| {
539            DrizzleError::ConversionError(format!("invalid UTF-8 in BLOB: {e}").into())
540        })?;
541        Ok(Self::new(s))
542    }
543}
544
545impl FromSQLiteValue for Box<String> {
546    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
547        String::from_sqlite_integer(value).map(Self::new)
548    }
549
550    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
551        String::from_sqlite_text(value).map(Self::new)
552    }
553
554    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
555        String::from_sqlite_real(value).map(Self::new)
556    }
557
558    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
559        String::from_sqlite_blob(value).map(Self::new)
560    }
561}
562
563impl FromSQLiteValue for Rc<String> {
564    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
565        String::from_sqlite_integer(value).map(Self::new)
566    }
567
568    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
569        String::from_sqlite_text(value).map(Self::new)
570    }
571
572    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
573        String::from_sqlite_real(value).map(Self::new)
574    }
575
576    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
577        String::from_sqlite_blob(value).map(Self::new)
578    }
579}
580
581impl FromSQLiteValue for Arc<String> {
582    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
583        String::from_sqlite_integer(value).map(Self::new)
584    }
585
586    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
587        String::from_sqlite_text(value).map(Self::new)
588    }
589
590    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
591        String::from_sqlite_real(value).map(Self::new)
592    }
593
594    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
595        String::from_sqlite_blob(value).map(Self::new)
596    }
597}
598
599impl FromSQLiteValue for Box<str> {
600    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
601        String::from_sqlite_integer(value).map(String::into_boxed_str)
602    }
603
604    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
605        String::from_sqlite_text(value).map(String::into_boxed_str)
606    }
607
608    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
609        String::from_sqlite_real(value).map(String::into_boxed_str)
610    }
611
612    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
613        String::from_sqlite_blob(value).map(String::into_boxed_str)
614    }
615}
616
617impl FromSQLiteValue for Rc<str> {
618    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
619        String::from_sqlite_integer(value).map(Self::from)
620    }
621
622    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
623        String::from_sqlite_text(value).map(Self::from)
624    }
625
626    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
627        String::from_sqlite_real(value).map(Self::from)
628    }
629
630    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
631        String::from_sqlite_blob(value).map(Self::from)
632    }
633}
634
635impl FromSQLiteValue for Arc<str> {
636    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
637        String::from_sqlite_integer(value).map(Self::from)
638    }
639
640    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
641        String::from_sqlite_text(value).map(Self::from)
642    }
643
644    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
645        String::from_sqlite_real(value).map(Self::from)
646    }
647
648    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
649        String::from_sqlite_blob(value).map(Self::from)
650    }
651}
652
653impl FromSQLiteValue for Vec<u8> {
654    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
655        Ok(value.to_le_bytes().to_vec())
656    }
657
658    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
659        Ok(value.as_bytes().to_vec())
660    }
661
662    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
663        Ok(value.to_le_bytes().to_vec())
664    }
665
666    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
667        Ok(value.to_vec())
668    }
669}
670
671#[cfg(feature = "bytes")]
672impl FromSQLiteValue for bytes::Bytes {
673    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
674        Vec::<u8>::from_sqlite_integer(value).map(Self::from)
675    }
676
677    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
678        Vec::<u8>::from_sqlite_text(value).map(Self::from)
679    }
680
681    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
682        Vec::<u8>::from_sqlite_real(value).map(Self::from)
683    }
684
685    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
686        Ok(Self::copy_from_slice(value))
687    }
688}
689
690#[cfg(feature = "bytes")]
691impl FromSQLiteValue for bytes::BytesMut {
692    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
693        Vec::<u8>::from_sqlite_integer(value).map(|v| Self::from(v.as_slice()))
694    }
695
696    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
697        Vec::<u8>::from_sqlite_text(value).map(|v| Self::from(v.as_slice()))
698    }
699
700    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
701        Vec::<u8>::from_sqlite_real(value).map(|v| Self::from(v.as_slice()))
702    }
703
704    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
705        Ok(Self::from(value))
706    }
707}
708
709#[cfg(feature = "smallvec")]
710impl<const N: usize> FromSQLiteValue for smallvec::SmallVec<[u8; N]> {
711    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
712        Err(DrizzleError::ConversionError(
713            "cannot convert INTEGER to SmallVec<u8>, use BLOB".into(),
714        ))
715    }
716
717    fn from_sqlite_text(_value: &str) -> Result<Self, DrizzleError> {
718        Err(DrizzleError::ConversionError(
719            "cannot convert TEXT to SmallVec<u8>, use BLOB".into(),
720        ))
721    }
722
723    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
724        Err(DrizzleError::ConversionError(
725            "cannot convert REAL to SmallVec<u8>, use BLOB".into(),
726        ))
727    }
728
729    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
730        let mut out = Self::new();
731        out.extend_from_slice(value);
732        Ok(out)
733    }
734}
735
736impl FromSQLiteValue for Box<Vec<u8>> {
737    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
738        Vec::<u8>::from_sqlite_integer(value).map(Self::new)
739    }
740
741    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
742        Vec::<u8>::from_sqlite_text(value).map(Self::new)
743    }
744
745    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
746        Vec::<u8>::from_sqlite_real(value).map(Self::new)
747    }
748
749    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
750        Vec::<u8>::from_sqlite_blob(value).map(Self::new)
751    }
752}
753
754impl FromSQLiteValue for Rc<Vec<u8>> {
755    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
756        Vec::<u8>::from_sqlite_integer(value).map(Self::new)
757    }
758
759    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
760        Vec::<u8>::from_sqlite_text(value).map(Self::new)
761    }
762
763    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
764        Vec::<u8>::from_sqlite_real(value).map(Self::new)
765    }
766
767    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
768        Vec::<u8>::from_sqlite_blob(value).map(Self::new)
769    }
770}
771
772impl FromSQLiteValue for Arc<Vec<u8>> {
773    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
774        Vec::<u8>::from_sqlite_integer(value).map(Self::new)
775    }
776
777    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
778        Vec::<u8>::from_sqlite_text(value).map(Self::new)
779    }
780
781    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
782        Vec::<u8>::from_sqlite_real(value).map(Self::new)
783    }
784
785    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
786        Vec::<u8>::from_sqlite_blob(value).map(Self::new)
787    }
788}
789
790// Option<T> implementation - handles NULL values
791impl<T: FromSQLiteValue> FromSQLiteValue for Option<T> {
792    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
793        T::from_sqlite_integer(value).map(Some)
794    }
795
796    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
797        T::from_sqlite_text(value).map(Some)
798    }
799
800    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
801        T::from_sqlite_real(value).map(Some)
802    }
803
804    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
805        T::from_sqlite_blob(value).map(Some)
806    }
807
808    fn from_sqlite_null() -> Result<Self, DrizzleError> {
809        Ok(None)
810    }
811}
812
813// =============================================================================
814// Driver-specific DrizzleRow implementations
815// =============================================================================
816
817#[cfg(feature = "rusqlite")]
818impl DrizzleRowByIndex for rusqlite::Row<'_> {
819    fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
820        let value_ref = self.get_ref(idx)?;
821        match value_ref {
822            rusqlite::types::ValueRef::Integer(i) => T::from_sqlite_integer(i),
823            rusqlite::types::ValueRef::Text(s) => {
824                let s = core::str::from_utf8(s).map_err(|e| {
825                    DrizzleError::ConversionError(format!("invalid UTF-8: {e}").into())
826                })?;
827                T::from_sqlite_text(s)
828            }
829            rusqlite::types::ValueRef::Real(r) => T::from_sqlite_real(r),
830            rusqlite::types::ValueRef::Blob(b) => T::from_sqlite_blob(b),
831            rusqlite::types::ValueRef::Null => T::from_sqlite_null(),
832        }
833    }
834}
835
836#[cfg(feature = "rusqlite")]
837impl DrizzleRowByName for rusqlite::Row<'_> {
838    fn get_column_by_name<T: FromSQLiteValue>(&self, name: &str) -> Result<T, DrizzleError> {
839        let idx = self.as_ref().column_index(name)?;
840        DrizzleRowByIndex::get_column(self, idx)
841    }
842}
843
844#[cfg(feature = "libsql")]
845impl DrizzleRowByIndex for libsql::Row {
846    fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
847        let idx_i32 = i32::try_from(idx).map_err(|e| {
848            DrizzleError::ConversionError(format!("column index {idx} out of range: {e}").into())
849        })?;
850        let value = self.get_value(idx_i32)?;
851        match value {
852            libsql::Value::Integer(i) => T::from_sqlite_integer(i),
853            libsql::Value::Text(ref s) => T::from_sqlite_text(s),
854            libsql::Value::Real(r) => T::from_sqlite_real(r),
855            libsql::Value::Blob(ref b) => T::from_sqlite_blob(b),
856            libsql::Value::Null => T::from_sqlite_null(),
857        }
858    }
859}
860
861#[cfg(feature = "libsql")]
862impl DrizzleRowByName for libsql::Row {
863    fn get_column_by_name<T: FromSQLiteValue>(&self, name: &str) -> Result<T, DrizzleError> {
864        let idx = (0..self.column_count())
865            .find(|&i| self.column_name(i) == Some(name))
866            .ok_or_else(|| {
867                DrizzleError::ConversionError(format!("column '{name}' not found").into())
868            })?;
869
870        let idx_usize = usize::try_from(idx).map_err(|e| {
871            DrizzleError::ConversionError(format!("column index {idx} negative: {e}").into())
872        })?;
873        DrizzleRowByIndex::get_column(self, idx_usize)
874    }
875}
876
877#[cfg(feature = "turso")]
878impl DrizzleRowByIndex for turso::Row {
879    fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
880        let value = self.get_value(idx)?;
881        if value.is_null() {
882            T::from_sqlite_null()
883        } else if let Some(&i) = value.as_integer() {
884            T::from_sqlite_integer(i)
885        } else if let Some(s) = value.as_text() {
886            T::from_sqlite_text(s)
887        } else if let Some(&r) = value.as_real() {
888            T::from_sqlite_real(r)
889        } else if let Some(b) = value.as_blob() {
890            T::from_sqlite_blob(b)
891        } else {
892            Err(DrizzleError::ConversionError(
893                "unknown SQLite value type".into(),
894            ))
895        }
896    }
897}
898
899// =============================================================================
900// UUID support (when feature enabled)
901// =============================================================================
902
903#[cfg(feature = "uuid")]
904impl FromSQLiteValue for uuid::Uuid {
905    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
906        Err(DrizzleError::ConversionError(
907            "cannot convert INTEGER to UUID".into(),
908        ))
909    }
910
911    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
912        Self::parse_str(value).map_err(|e| {
913            DrizzleError::ConversionError(format!("invalid UUID string '{value}': {e}").into())
914        })
915    }
916
917    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
918        Err(DrizzleError::ConversionError(
919            "cannot convert REAL to UUID".into(),
920        ))
921    }
922
923    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
924        Self::from_slice(value)
925            .map_err(|e| DrizzleError::ConversionError(format!("invalid UUID bytes: {e}").into()))
926    }
927}
928
929// =============================================================================
930// Chrono date/time types (parse from ISO-8601 text)
931// =============================================================================
932
933#[cfg(feature = "chrono")]
934impl FromSQLiteValue for chrono::NaiveDate {
935    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
936        Err(DrizzleError::ConversionError(
937            "cannot convert INTEGER to NaiveDate".into(),
938        ))
939    }
940
941    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
942        value.parse().map_err(|e| {
943            DrizzleError::ConversionError(
944                format!("cannot parse '{value}' as NaiveDate: {e}").into(),
945            )
946        })
947    }
948
949    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
950        Err(DrizzleError::ConversionError(
951            "cannot convert REAL to NaiveDate".into(),
952        ))
953    }
954
955    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
956        Err(DrizzleError::ConversionError(
957            "cannot convert BLOB to NaiveDate".into(),
958        ))
959    }
960}
961
962#[cfg(feature = "chrono")]
963impl FromSQLiteValue for chrono::NaiveTime {
964    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
965        Err(DrizzleError::ConversionError(
966            "cannot convert INTEGER to NaiveTime".into(),
967        ))
968    }
969
970    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
971        value.parse().map_err(|e| {
972            DrizzleError::ConversionError(
973                format!("cannot parse '{value}' as NaiveTime: {e}").into(),
974            )
975        })
976    }
977
978    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
979        Err(DrizzleError::ConversionError(
980            "cannot convert REAL to NaiveTime".into(),
981        ))
982    }
983
984    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
985        Err(DrizzleError::ConversionError(
986            "cannot convert BLOB to NaiveTime".into(),
987        ))
988    }
989}
990
991#[cfg(feature = "chrono")]
992impl FromSQLiteValue for chrono::NaiveDateTime {
993    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
994        Err(DrizzleError::ConversionError(
995            "cannot convert INTEGER to NaiveDateTime".into(),
996        ))
997    }
998
999    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1000        value.parse().map_err(|e| {
1001            DrizzleError::ConversionError(
1002                format!("cannot parse '{value}' as NaiveDateTime: {e}").into(),
1003            )
1004        })
1005    }
1006
1007    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1008        Err(DrizzleError::ConversionError(
1009            "cannot convert REAL to NaiveDateTime".into(),
1010        ))
1011    }
1012
1013    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1014        Err(DrizzleError::ConversionError(
1015            "cannot convert BLOB to NaiveDateTime".into(),
1016        ))
1017    }
1018}
1019
1020#[cfg(feature = "chrono")]
1021impl FromSQLiteValue for chrono::DateTime<chrono::FixedOffset> {
1022    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1023        Err(DrizzleError::ConversionError(
1024            "cannot convert INTEGER to DateTime<FixedOffset>".into(),
1025        ))
1026    }
1027
1028    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1029        Self::parse_from_rfc3339(value).map_err(|e| {
1030            DrizzleError::ConversionError(
1031                format!("cannot parse '{value}' as DateTime<FixedOffset>: {e}").into(),
1032            )
1033        })
1034    }
1035
1036    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1037        Err(DrizzleError::ConversionError(
1038            "cannot convert REAL to DateTime<FixedOffset>".into(),
1039        ))
1040    }
1041
1042    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1043        Err(DrizzleError::ConversionError(
1044            "cannot convert BLOB to DateTime<FixedOffset>".into(),
1045        ))
1046    }
1047}
1048
1049#[cfg(feature = "chrono")]
1050impl FromSQLiteValue for chrono::DateTime<chrono::Utc> {
1051    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1052        Err(DrizzleError::ConversionError(
1053            "cannot convert INTEGER to DateTime<Utc>".into(),
1054        ))
1055    }
1056
1057    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1058        value.parse().map_err(|e| {
1059            DrizzleError::ConversionError(
1060                format!("cannot parse '{value}' as DateTime<Utc>: {e}").into(),
1061            )
1062        })
1063    }
1064
1065    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1066        Err(DrizzleError::ConversionError(
1067            "cannot convert REAL to DateTime<Utc>".into(),
1068        ))
1069    }
1070
1071    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1072        Err(DrizzleError::ConversionError(
1073            "cannot convert BLOB to DateTime<Utc>".into(),
1074        ))
1075    }
1076}
1077
1078// =============================================================================
1079// Time crate date/time types (parse from ISO-8601 text)
1080// =============================================================================
1081
1082#[cfg(feature = "time")]
1083impl FromSQLiteValue for time::Date {
1084    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1085        Err(DrizzleError::ConversionError(
1086            "cannot convert INTEGER to time::Date".into(),
1087        ))
1088    }
1089
1090    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1091        Self::parse(value, &time::format_description::well_known::Iso8601::DATE).map_err(|e| {
1092            DrizzleError::ConversionError(
1093                format!("cannot parse '{value}' as time::Date: {e}").into(),
1094            )
1095        })
1096    }
1097
1098    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1099        Err(DrizzleError::ConversionError(
1100            "cannot convert REAL to time::Date".into(),
1101        ))
1102    }
1103
1104    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1105        Err(DrizzleError::ConversionError(
1106            "cannot convert BLOB to time::Date".into(),
1107        ))
1108    }
1109}
1110
1111#[cfg(feature = "time")]
1112impl FromSQLiteValue for time::Time {
1113    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1114        Err(DrizzleError::ConversionError(
1115            "cannot convert INTEGER to time::Time".into(),
1116        ))
1117    }
1118
1119    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1120        Self::parse(value, &time::format_description::well_known::Iso8601::TIME).map_err(|e| {
1121            DrizzleError::ConversionError(
1122                format!("cannot parse '{value}' as time::Time: {e}").into(),
1123            )
1124        })
1125    }
1126
1127    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1128        Err(DrizzleError::ConversionError(
1129            "cannot convert REAL to time::Time".into(),
1130        ))
1131    }
1132
1133    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1134        Err(DrizzleError::ConversionError(
1135            "cannot convert BLOB to time::Time".into(),
1136        ))
1137    }
1138}
1139
1140#[cfg(feature = "time")]
1141impl FromSQLiteValue for time::PrimitiveDateTime {
1142    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1143        Err(DrizzleError::ConversionError(
1144            "cannot convert INTEGER to time::PrimitiveDateTime".into(),
1145        ))
1146    }
1147
1148    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1149        Self::parse(
1150            value,
1151            &time::format_description::well_known::Iso8601::DATE_TIME,
1152        )
1153        .map_err(|e| {
1154            DrizzleError::ConversionError(
1155                format!("cannot parse '{value}' as time::PrimitiveDateTime: {e}").into(),
1156            )
1157        })
1158    }
1159
1160    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1161        Err(DrizzleError::ConversionError(
1162            "cannot convert REAL to time::PrimitiveDateTime".into(),
1163        ))
1164    }
1165
1166    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1167        Err(DrizzleError::ConversionError(
1168            "cannot convert BLOB to time::PrimitiveDateTime".into(),
1169        ))
1170    }
1171}
1172
1173#[cfg(feature = "time")]
1174impl FromSQLiteValue for time::OffsetDateTime {
1175    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1176        Err(DrizzleError::ConversionError(
1177            "cannot convert INTEGER to time::OffsetDateTime".into(),
1178        ))
1179    }
1180
1181    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1182        Self::parse(value, &time::format_description::well_known::Rfc3339).map_err(|e| {
1183            DrizzleError::ConversionError(
1184                format!("cannot parse '{value}' as time::OffsetDateTime: {e}").into(),
1185            )
1186        })
1187    }
1188
1189    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1190        Err(DrizzleError::ConversionError(
1191            "cannot convert REAL to time::OffsetDateTime".into(),
1192        ))
1193    }
1194
1195    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1196        Err(DrizzleError::ConversionError(
1197            "cannot convert BLOB to time::OffsetDateTime".into(),
1198        ))
1199    }
1200}
1201
1202// =============================================================================
1203// Decimal (parse from text)
1204// =============================================================================
1205
1206#[cfg(feature = "rust-decimal")]
1207impl FromSQLiteValue for rust_decimal::Decimal {
1208    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
1209        Ok(Self::from(value))
1210    }
1211
1212    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1213        value.parse().map_err(|e| {
1214            DrizzleError::ConversionError(format!("cannot parse '{value}' as Decimal: {e}").into())
1215        })
1216    }
1217
1218    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
1219        Self::try_from(value).map_err(|e| {
1220            DrizzleError::ConversionError(
1221                format!("cannot convert REAL {value} to Decimal: {e}").into(),
1222            )
1223        })
1224    }
1225
1226    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1227        Err(DrizzleError::ConversionError(
1228            "cannot convert BLOB to Decimal".into(),
1229        ))
1230    }
1231}
1232
1233// =============================================================================
1234// Duration types
1235// =============================================================================
1236
1237#[cfg(feature = "chrono")]
1238impl FromSQLiteValue for chrono::Duration {
1239    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
1240        Ok(Self::seconds(value))
1241    }
1242
1243    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1244        // Parse seconds from the text representation
1245        let secs: i64 = value.trim_end_matches('s').parse().map_err(|e| {
1246            DrizzleError::ConversionError(
1247                format!("cannot parse '{value}' as chrono::Duration: {e}").into(),
1248            )
1249        })?;
1250        Ok(Self::seconds(secs))
1251    }
1252
1253    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
1254        let millis: i64 = format!("{:.0}", value * 1000.0).parse().map_err(|e| {
1255            DrizzleError::ConversionError(
1256                format!("cannot convert REAL {value} to chrono::Duration: {e}").into(),
1257            )
1258        })?;
1259        Ok(Self::milliseconds(millis))
1260    }
1261
1262    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1263        Err(DrizzleError::ConversionError(
1264            "cannot convert BLOB to chrono::Duration".into(),
1265        ))
1266    }
1267}
1268
1269#[cfg(feature = "time")]
1270impl FromSQLiteValue for time::Duration {
1271    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
1272        Ok(Self::seconds(value))
1273    }
1274
1275    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1276        // Parse seconds from the "Ns" text representation
1277        let secs: i64 = value.trim_end_matches('s').parse().map_err(|e| {
1278            DrizzleError::ConversionError(
1279                format!("cannot parse '{value}' as time::Duration: {e}").into(),
1280            )
1281        })?;
1282        Ok(Self::seconds(secs))
1283    }
1284
1285    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
1286        Ok(Self::seconds_f64(value))
1287    }
1288
1289    fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
1290        Err(DrizzleError::ConversionError(
1291            "cannot convert BLOB to time::Duration".into(),
1292        ))
1293    }
1294}
1295
1296// =============================================================================
1297// JSON support (serde_json::Value)
1298// =============================================================================
1299
1300#[cfg(feature = "serde")]
1301impl FromSQLiteValue for serde_json::Value {
1302    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
1303        Ok(Self::Number(value.into()))
1304    }
1305
1306    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1307        serde_json::from_str(value).map_err(|e| {
1308            DrizzleError::ConversionError(format!("cannot parse '{value}' as JSON: {e}").into())
1309        })
1310    }
1311
1312    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
1313        serde_json::Number::from_f64(value)
1314            .map(serde_json::Value::Number)
1315            .ok_or_else(|| {
1316                DrizzleError::ConversionError(
1317                    format!("cannot convert non-finite REAL {value} to JSON").into(),
1318                )
1319            })
1320    }
1321
1322    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
1323        serde_json::from_slice(value).map_err(|e| {
1324            DrizzleError::ConversionError(format!("cannot parse BLOB as JSON: {e}").into())
1325        })
1326    }
1327}
1328
1329// =============================================================================
1330// ArrayVec/ArrayString support
1331// =============================================================================
1332
1333#[cfg(feature = "arrayvec")]
1334impl<const N: usize> FromSQLiteValue for arrayvec::ArrayString<N> {
1335    fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
1336        let s = value.to_string();
1337        Self::from(&s).map_err(|_| {
1338            DrizzleError::ConversionError(
1339                format!(
1340                    "String length {} exceeds ArrayString capacity {}",
1341                    s.len(),
1342                    N
1343                )
1344                .into(),
1345            )
1346        })
1347    }
1348
1349    fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
1350        Self::from(value).map_err(|_| {
1351            DrizzleError::ConversionError(
1352                format!(
1353                    "Text length {} exceeds ArrayString capacity {}",
1354                    value.len(),
1355                    N
1356                )
1357                .into(),
1358            )
1359        })
1360    }
1361
1362    fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
1363        let s = value.to_string();
1364        Self::from(&s).map_err(|_| {
1365            DrizzleError::ConversionError(
1366                format!(
1367                    "String length {} exceeds ArrayString capacity {}",
1368                    s.len(),
1369                    N
1370                )
1371                .into(),
1372            )
1373        })
1374    }
1375
1376    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
1377        let s = String::from_utf8(value.to_vec())
1378            .map_err(|e| DrizzleError::ConversionError(format!("invalid UTF-8: {e}").into()))?;
1379        Self::from(&s).map_err(|_| {
1380            DrizzleError::ConversionError(
1381                format!(
1382                    "String length {} exceeds ArrayString capacity {}",
1383                    s.len(),
1384                    N
1385                )
1386                .into(),
1387            )
1388        })
1389    }
1390}
1391
1392#[cfg(feature = "arrayvec")]
1393impl<const N: usize> FromSQLiteValue for arrayvec::ArrayVec<u8, N> {
1394    fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
1395        Err(DrizzleError::ConversionError(
1396            "cannot convert INTEGER to ArrayVec<u8>, use BLOB".into(),
1397        ))
1398    }
1399
1400    fn from_sqlite_text(_value: &str) -> Result<Self, DrizzleError> {
1401        Err(DrizzleError::ConversionError(
1402            "cannot convert TEXT to ArrayVec<u8>, use BLOB".into(),
1403        ))
1404    }
1405
1406    fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
1407        Err(DrizzleError::ConversionError(
1408            "cannot convert REAL to ArrayVec<u8>, use BLOB".into(),
1409        ))
1410    }
1411
1412    fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
1413        Self::try_from(value).map_err(|_| {
1414            DrizzleError::ConversionError(
1415                format!(
1416                    "Blob length {} exceeds ArrayVec capacity {}",
1417                    value.len(),
1418                    N
1419                )
1420                .into(),
1421            )
1422        })
1423    }
1424}