drizzle_sqlite/
common.rs

1use drizzle_core::{SQL, ToSQL, traits::SQLParam};
2
3//------------------------------------------------------------------------------
4// Number Type
5//------------------------------------------------------------------------------
6
7/// Numeric type that can be either an integer or a floating point value
8#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
9pub enum Number {
10    /// Integer value
11    Integer(i64),
12    /// Floating point value
13    Real(f64),
14}
15
16impl Default for Number {
17    fn default() -> Self {
18        Self::Integer(Default::default())
19    }
20}
21
22impl From<i64> for Number {
23    fn from(value: i64) -> Self {
24        Self::Integer(value)
25    }
26}
27
28impl From<f64> for Number {
29    fn from(value: f64) -> Self {
30        Self::Real(value)
31    }
32}
33
34// Note: Generic From implementation is removed to avoid conflicts.
35// The table macro will generate specific implementations using SQLiteEnumVisitor.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
37pub enum JoinType {
38    #[default]
39    Join,
40    Inner,
41    Left,
42    Right,
43    Full,
44    Cross,
45}
46
47#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
48pub struct Join {
49    pub natural: bool,
50    pub join_type: JoinType,
51    pub outer: bool, // only meaningful for LEFT/RIGHT/FULL
52}
53
54impl Join {
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    pub fn natural(mut self) -> Self {
60        self.natural = true;
61        self
62    }
63
64    pub fn inner(mut self) -> Self {
65        self.join_type = JoinType::Inner;
66        self
67    }
68
69    pub fn left(mut self) -> Self {
70        self.join_type = JoinType::Left;
71        self
72    }
73
74    pub fn right(mut self) -> Self {
75        self.join_type = JoinType::Right;
76        self
77    }
78
79    pub fn full(mut self) -> Self {
80        self.join_type = JoinType::Full;
81        self
82    }
83
84    pub fn cross(mut self) -> Self {
85        self.join_type = JoinType::Cross;
86        self
87    }
88
89    pub fn outer(mut self) -> Self {
90        self.outer = true;
91        self
92    }
93}
94impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Join {
95    fn to_sql(&self) -> SQL<'a, V> {
96        let mut parts = Vec::new();
97
98        if self.natural {
99            parts.push("NATURAL");
100        }
101
102        match self.join_type {
103            JoinType::Inner => parts.push("INNER"),
104            JoinType::Left => {
105                parts.push("LEFT");
106                if self.outer {
107                    parts.push("OUTER");
108                }
109            }
110            JoinType::Right => {
111                parts.push("RIGHT");
112                if self.outer {
113                    parts.push("OUTER");
114                }
115            }
116            JoinType::Full => {
117                parts.push("FULL");
118                if self.outer {
119                    parts.push("OUTER");
120                }
121            }
122            JoinType::Cross => parts.push("CROSS"),
123            JoinType::Join => {}
124        }
125
126        parts.push("JOIN");
127        SQL::raw(parts.join(" "))
128    }
129}
130
131//------------------------------------------------------------------------------
132// Tests
133//------------------------------------------------------------------------------
134
135#[cfg(any(feature = "turso", feature = "libsql", feature = "rusqlite"))]
136#[cfg(test)]
137mod tests {
138    use drizzle_rs::prelude::*;
139    use std::borrow::Cow;
140
141    // Define test enums for SQLiteEnum tests
142    // Text-based enum (default)
143    #[derive(SQLiteEnum, Default, Debug, Clone, PartialEq)]
144    enum Role {
145        #[default]
146        User,
147        Admin,
148        Moderator,
149    }
150
151    // Integer-based enum with explicit discriminants
152    #[derive(SQLiteEnum, Default, Debug, Clone, PartialEq)]
153    enum Status {
154        Active = 1,
155        #[default]
156        Inactive = 0,
157        Banned = -1,
158    }
159
160    #[test]
161    fn test_into_sqlite_value_impls() {
162        assert_eq!(
163            SQLiteValue::from("hello"),
164            SQLiteValue::Text(Cow::Borrowed("hello"))
165        );
166        assert_eq!(
167            SQLiteValue::from(String::from("world")),
168            SQLiteValue::Text(Cow::Owned("world".to_string()))
169        );
170        assert_eq!(SQLiteValue::from(42i64), SQLiteValue::Integer(42));
171        assert_eq!(SQLiteValue::from(123i32), SQLiteValue::Integer(123));
172        assert_eq!(SQLiteValue::from(3.14f64), SQLiteValue::Real(3.14));
173        assert_eq!(SQLiteValue::from(true), SQLiteValue::Integer(1));
174        assert_eq!(SQLiteValue::from(false), SQLiteValue::Integer(0));
175        let blob_vec: Vec<u8> = vec![1, 2, 3];
176        assert_eq!(
177            SQLiteValue::from(blob_vec.clone()),
178            SQLiteValue::Blob(Cow::Owned(blob_vec.clone()))
179        );
180        let blob_slice: &[u8] = &[4, 5, 6];
181        assert_eq!(
182            SQLiteValue::from(blob_slice),
183            SQLiteValue::Blob(Cow::Borrowed(blob_slice))
184        );
185        assert_eq!(SQLiteValue::from(Option::<String>::None), SQLiteValue::Null);
186        assert_eq!(
187            SQLiteValue::from(Some("optional")),
188            SQLiteValue::Text(Cow::Borrowed("optional"))
189        );
190    }
191
192    #[test]
193    fn test_sqlite_enum_derives() {
194        // Just test that the enums compile and basic functionality works
195        let role = Role::Admin;
196        let status = Status::Active;
197
198        // Test that Display works
199        assert_eq!(format!("{}", role), "Admin");
200        assert_eq!(format!("{}", status), "Active");
201
202        // Test that FromStr works
203        assert_eq!("User".parse::<Role>().unwrap(), Role::User);
204        assert_eq!("Banned".parse::<Status>().unwrap(), Status::Banned);
205    }
206}