1use drizzle_core::{SQL, ToSQL, traits::SQLParam};
2
3#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
9pub enum Number {
10 Integer(i64),
12 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#[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, }
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#[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}