sql_middleware/
types.rs

1use chrono::NaiveDateTime;
2use clap::ValueEnum;
3use serde_json::Value as JsonValue;
4
5use crate::error::SqlMiddlewareDbError;
6
7/// Values that can be stored in a database row or used as query parameters
8///
9/// This enum provides a unified representation of database values across
10/// different database engines.
11#[derive(Debug, Clone, PartialEq)]
12pub enum RowValues {
13    /// Integer value (64-bit)
14    Int(i64),
15    /// Floating point value (64-bit)
16    Float(f64),
17    /// Text/string value
18    Text(String),
19    /// Boolean value
20    Bool(bool),
21    /// Timestamp value
22    Timestamp(NaiveDateTime),
23    /// NULL value
24    Null,
25    /// JSON value
26    JSON(JsonValue),
27    /// Binary data
28    Blob(Vec<u8>),
29}
30
31impl RowValues {
32    /// Check if this value is NULL
33    #[must_use]
34    pub fn is_null(&self) -> bool {
35        matches!(self, Self::Null)
36    }
37
38    #[must_use]
39    pub fn as_int(&self) -> Option<&i64> {
40        if let RowValues::Int(value) = self {
41            Some(value)
42        } else {
43            None
44        }
45    }
46
47    #[must_use]
48    pub fn as_text(&self) -> Option<&str> {
49        if let RowValues::Text(value) = self {
50            Some(value)
51        } else {
52            None
53        }
54    }
55
56    #[must_use]
57    pub fn as_bool(&self) -> Option<&bool> {
58        if let RowValues::Bool(value) = self {
59            return Some(value);
60        } else if let Some(i) = self.as_int() {
61            if *i == 1 {
62                return Some(&true);
63            } else if *i == 0 {
64                return Some(&false);
65            }
66        }
67        None
68    }
69
70    #[must_use]
71    pub fn as_timestamp(&self) -> Option<chrono::NaiveDateTime> {
72        if let RowValues::Timestamp(value) = self {
73            return Some(*value);
74        } else if let Some(s) = self.as_text() {
75            // Try "YYYY-MM-DD HH:MM:SS"
76            if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
77                return Some(dt);
78            }
79            // Try "YYYY-MM-DD HH:MM:SS.SSS"
80            if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S.%3f") {
81                return Some(dt);
82            }
83        }
84        None
85    }
86
87    #[must_use]
88    pub fn as_float(&self) -> Option<f64> {
89        if let RowValues::Float(value) = self {
90            Some(*value)
91        } else {
92            None
93        }
94    }
95
96    #[must_use]
97    pub fn as_blob(&self) -> Option<&[u8]> {
98        if let RowValues::Blob(bytes) = self {
99            Some(bytes)
100        } else {
101            None
102        }
103    }
104}
105
106/// The database type supported by this middleware
107#[derive(Debug, Clone, PartialEq, Eq, Hash, ValueEnum)]
108pub enum DatabaseType {
109    /// `PostgreSQL` database
110    #[cfg(feature = "postgres")]
111    Postgres,
112    /// `SQLite` database
113    #[cfg(feature = "sqlite")]
114    Sqlite,
115    /// SQL Server database
116    #[cfg(feature = "mssql")]
117    Mssql,
118    /// `LibSQL` database
119    #[cfg(feature = "libsql")]
120    Libsql,
121    /// Turso (SQLite-compatible, in-process) database
122    #[cfg(feature = "turso")]
123    Turso,
124}
125
126/// The conversion "mode".
127#[derive(Debug, Clone, Copy, PartialEq)]
128pub enum ConversionMode {
129    /// When the converted parameters will be used in a query (SELECT)
130    Query,
131    /// When the converted parameters will be used for statement execution (INSERT/UPDATE/etc.)
132    Execute,
133}
134
135/// Convert a slice of `RowValues` into database-specific parameters.
136/// This trait provides a unified interface for converting generic `RowValues`
137/// to database-specific parameter types.
138pub trait ParamConverter<'a> {
139    type Converted;
140
141    /// Convert a slice of `RowValues` into the backend's parameter type.
142    ///
143    /// # Errors
144    ///
145    /// Returns `SqlMiddlewareDbError` if the conversion fails for any parameter.
146    fn convert_sql_params(
147        params: &'a [RowValues],
148        mode: ConversionMode,
149    ) -> Result<Self::Converted, SqlMiddlewareDbError>;
150
151    /// Check if this converter supports the given mode
152    ///
153    /// # Arguments
154    /// * `mode` - The conversion mode to check
155    ///
156    /// # Returns
157    /// * `bool` - Whether this converter supports the mode
158    #[must_use]
159    fn supports_mode(_mode: ConversionMode) -> bool {
160        true // By default, support both modes
161    }
162}