1#![allow(clippy::expect_used)]
5
6use crate::error::TypeError;
7use crate::value::SqlValue;
8
9pub trait ToSql {
14 fn to_sql(&self) -> Result<SqlValue, TypeError>;
16
17 fn sql_type(&self) -> &'static str;
19}
20
21impl ToSql for bool {
22 fn to_sql(&self) -> Result<SqlValue, TypeError> {
23 Ok(SqlValue::Bool(*self))
24 }
25
26 fn sql_type(&self) -> &'static str {
27 "BIT"
28 }
29}
30
31impl ToSql for u8 {
32 fn to_sql(&self) -> Result<SqlValue, TypeError> {
33 Ok(SqlValue::TinyInt(*self))
34 }
35
36 fn sql_type(&self) -> &'static str {
37 "TINYINT"
38 }
39}
40
41impl ToSql for i16 {
42 fn to_sql(&self) -> Result<SqlValue, TypeError> {
43 Ok(SqlValue::SmallInt(*self))
44 }
45
46 fn sql_type(&self) -> &'static str {
47 "SMALLINT"
48 }
49}
50
51impl ToSql for i32 {
52 fn to_sql(&self) -> Result<SqlValue, TypeError> {
53 Ok(SqlValue::Int(*self))
54 }
55
56 fn sql_type(&self) -> &'static str {
57 "INT"
58 }
59}
60
61impl ToSql for i64 {
62 fn to_sql(&self) -> Result<SqlValue, TypeError> {
63 Ok(SqlValue::BigInt(*self))
64 }
65
66 fn sql_type(&self) -> &'static str {
67 "BIGINT"
68 }
69}
70
71impl ToSql for f32 {
72 fn to_sql(&self) -> Result<SqlValue, TypeError> {
73 Ok(SqlValue::Float(*self))
74 }
75
76 fn sql_type(&self) -> &'static str {
77 "REAL"
78 }
79}
80
81impl ToSql for f64 {
82 fn to_sql(&self) -> Result<SqlValue, TypeError> {
83 Ok(SqlValue::Double(*self))
84 }
85
86 fn sql_type(&self) -> &'static str {
87 "FLOAT"
88 }
89}
90
91impl ToSql for str {
92 fn to_sql(&self) -> Result<SqlValue, TypeError> {
93 Ok(SqlValue::String(self.to_owned()))
94 }
95
96 fn sql_type(&self) -> &'static str {
97 "NVARCHAR"
98 }
99}
100
101impl ToSql for String {
102 fn to_sql(&self) -> Result<SqlValue, TypeError> {
103 Ok(SqlValue::String(self.clone()))
104 }
105
106 fn sql_type(&self) -> &'static str {
107 "NVARCHAR"
108 }
109}
110
111impl ToSql for [u8] {
112 fn to_sql(&self) -> Result<SqlValue, TypeError> {
113 Ok(SqlValue::Binary(bytes::Bytes::copy_from_slice(self)))
114 }
115
116 fn sql_type(&self) -> &'static str {
117 "VARBINARY"
118 }
119}
120
121impl ToSql for Vec<u8> {
122 fn to_sql(&self) -> Result<SqlValue, TypeError> {
123 Ok(SqlValue::Binary(bytes::Bytes::copy_from_slice(self)))
124 }
125
126 fn sql_type(&self) -> &'static str {
127 "VARBINARY"
128 }
129}
130
131impl<T: ToSql> ToSql for Option<T> {
132 fn to_sql(&self) -> Result<SqlValue, TypeError> {
133 match self {
134 Some(v) => v.to_sql(),
135 None => Ok(SqlValue::Null),
136 }
137 }
138
139 fn sql_type(&self) -> &'static str {
140 match self {
141 Some(v) => v.sql_type(),
142 None => "NULL",
143 }
144 }
145}
146
147impl<T: ToSql + ?Sized> ToSql for &T {
148 fn to_sql(&self) -> Result<SqlValue, TypeError> {
149 (*self).to_sql()
150 }
151
152 fn sql_type(&self) -> &'static str {
153 (*self).sql_type()
154 }
155}
156
157#[cfg(feature = "uuid")]
158impl ToSql for uuid::Uuid {
159 fn to_sql(&self) -> Result<SqlValue, TypeError> {
160 Ok(SqlValue::Uuid(*self))
161 }
162
163 fn sql_type(&self) -> &'static str {
164 "UNIQUEIDENTIFIER"
165 }
166}
167
168#[cfg(feature = "decimal")]
169impl ToSql for rust_decimal::Decimal {
170 fn to_sql(&self) -> Result<SqlValue, TypeError> {
171 Ok(SqlValue::Decimal(*self))
172 }
173
174 fn sql_type(&self) -> &'static str {
175 "DECIMAL"
176 }
177}
178
179#[cfg(feature = "decimal")]
180impl ToSql for crate::value::Money {
181 fn to_sql(&self) -> Result<SqlValue, TypeError> {
182 Ok(SqlValue::Money(self.0))
183 }
184
185 fn sql_type(&self) -> &'static str {
186 "MONEY"
187 }
188}
189
190#[cfg(feature = "decimal")]
191impl ToSql for crate::value::SmallMoney {
192 fn to_sql(&self) -> Result<SqlValue, TypeError> {
193 Ok(SqlValue::SmallMoney(self.0))
194 }
195
196 fn sql_type(&self) -> &'static str {
197 "SMALLMONEY"
198 }
199}
200
201#[cfg(feature = "chrono")]
202impl ToSql for chrono::NaiveDate {
203 fn to_sql(&self) -> Result<SqlValue, TypeError> {
204 Ok(SqlValue::Date(*self))
205 }
206
207 fn sql_type(&self) -> &'static str {
208 "DATE"
209 }
210}
211
212#[cfg(feature = "chrono")]
213impl ToSql for chrono::NaiveTime {
214 fn to_sql(&self) -> Result<SqlValue, TypeError> {
215 Ok(SqlValue::Time(*self))
216 }
217
218 fn sql_type(&self) -> &'static str {
219 "TIME"
220 }
221}
222
223#[cfg(feature = "chrono")]
224impl ToSql for chrono::NaiveDateTime {
225 fn to_sql(&self) -> Result<SqlValue, TypeError> {
226 Ok(SqlValue::DateTime(*self))
227 }
228
229 fn sql_type(&self) -> &'static str {
230 "DATETIME2"
231 }
232}
233
234#[cfg(feature = "chrono")]
235impl ToSql for crate::value::SmallDateTime {
236 fn to_sql(&self) -> Result<SqlValue, TypeError> {
237 Ok(SqlValue::SmallDateTime(self.0))
238 }
239
240 fn sql_type(&self) -> &'static str {
241 "SMALLDATETIME"
242 }
243}
244
245#[cfg(feature = "chrono")]
246impl ToSql for chrono::DateTime<chrono::FixedOffset> {
247 fn to_sql(&self) -> Result<SqlValue, TypeError> {
248 Ok(SqlValue::DateTimeOffset(*self))
249 }
250
251 fn sql_type(&self) -> &'static str {
252 "DATETIMEOFFSET"
253 }
254}
255
256#[cfg(feature = "chrono")]
257impl ToSql for chrono::DateTime<chrono::Utc> {
258 fn to_sql(&self) -> Result<SqlValue, TypeError> {
259 let fixed = self.with_timezone(&chrono::FixedOffset::east_opt(0).expect("valid offset"));
261 Ok(SqlValue::DateTimeOffset(fixed))
262 }
263
264 fn sql_type(&self) -> &'static str {
265 "DATETIMEOFFSET"
266 }
267}
268
269#[cfg(feature = "json")]
270impl ToSql for serde_json::Value {
271 fn to_sql(&self) -> Result<SqlValue, TypeError> {
272 Ok(SqlValue::Json(self.clone()))
273 }
274
275 fn sql_type(&self) -> &'static str {
276 "NVARCHAR(MAX)"
277 }
278}
279
280#[cfg(test)]
281#[allow(clippy::unwrap_used)]
282mod tests {
283 use super::*;
284
285 #[test]
286 fn test_to_sql_i32() {
287 let value: i32 = 42;
288 assert_eq!(value.to_sql().unwrap(), SqlValue::Int(42));
289 assert_eq!(value.sql_type(), "INT");
290 }
291
292 #[test]
293 fn test_to_sql_string() {
294 let value = "hello".to_string();
295 assert_eq!(
296 value.to_sql().unwrap(),
297 SqlValue::String("hello".to_string())
298 );
299 assert_eq!(value.sql_type(), "NVARCHAR");
300 }
301
302 #[test]
303 fn test_to_sql_option() {
304 let some: Option<i32> = Some(42);
305 assert_eq!(some.to_sql().unwrap(), SqlValue::Int(42));
306
307 let none: Option<i32> = None;
308 assert_eq!(none.to_sql().unwrap(), SqlValue::Null);
309 }
310}