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 crate::*;
139    use crate::common::{Number, Join, JoinType};
140    use std::borrow::Cow;
141
142    #[test]
143    fn test_into_sqlite_value_impls() {
144        assert_eq!(
145            SQLiteValue::from("hello"),
146            SQLiteValue::Text(Cow::Borrowed("hello"))
147        );
148        assert_eq!(
149            SQLiteValue::from(String::from("world")),
150            SQLiteValue::Text(Cow::Owned("world".to_string()))
151        );
152        assert_eq!(SQLiteValue::from(42i64), SQLiteValue::Integer(42));
153        assert_eq!(SQLiteValue::from(123i32), SQLiteValue::Integer(123));
154        assert_eq!(SQLiteValue::from(3.14f64), SQLiteValue::Real(3.14));
155        assert_eq!(SQLiteValue::from(true), SQLiteValue::Integer(1));
156        assert_eq!(SQLiteValue::from(false), SQLiteValue::Integer(0));
157        let blob_vec: Vec<u8> = vec![1, 2, 3];
158        assert_eq!(
159            SQLiteValue::from(blob_vec.clone()),
160            SQLiteValue::Blob(Cow::Owned(blob_vec.clone()))
161        );
162        let blob_slice: &[u8] = &[4, 5, 6];
163        assert_eq!(
164            SQLiteValue::from(blob_slice),
165            SQLiteValue::Blob(Cow::Borrowed(blob_slice))
166        );
167        assert_eq!(SQLiteValue::from(Option::<String>::None), SQLiteValue::Null);
168        assert_eq!(
169            SQLiteValue::from(Some("optional")),
170            SQLiteValue::Text(Cow::Borrowed("optional"))
171        );
172    }
173
174    #[test]
175    fn test_number_enum() {
176        let int_num = Number::Integer(42);
177        let real_num = Number::Real(3.14);
178        
179        assert_eq!(int_num, Number::from(42i64));
180        assert_eq!(real_num, Number::from(3.14f64));
181        assert_eq!(Number::default(), Number::Integer(0));
182    }
183
184    #[test]
185    fn test_join_type_and_join() {
186        let join = Join::new().inner().natural();
187        assert_eq!(join.join_type, JoinType::Inner);
188        assert_eq!(join.natural, true);
189        assert_eq!(join.outer, false);
190
191        let outer_join = Join::new().left().outer();
192        assert_eq!(outer_join.join_type, JoinType::Left);
193        assert_eq!(outer_join.outer, true);
194        
195        let cross_join = Join::new().cross();
196        assert_eq!(cross_join.join_type, JoinType::Cross);
197    }
198
199    #[test]
200    fn test_join_to_sql() {
201        use drizzle_core::{ToSQL, SQL};
202        
203        let inner_join = Join::new().inner();
204        let sql: SQL<SQLiteValue> = inner_join.to_sql();
205        assert_eq!(sql.sql(), "INNER JOIN");
206
207        let natural_left_outer = Join::new().natural().left().outer();
208        let sql: SQL<SQLiteValue> = natural_left_outer.to_sql();
209        assert_eq!(sql.sql(), "NATURAL LEFT OUTER JOIN");
210
211        let cross_join = Join::new().cross();
212        let sql: SQL<SQLiteValue> = cross_join.to_sql();
213        assert_eq!(sql.sql(), "CROSS JOIN");
214    }
215}