1use drizzle_core::error::DrizzleError;
7
8pub trait FromSQLiteValue: Sized {
19 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError>;
21
22 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError>;
24
25 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError>;
27
28 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError>;
30
31 fn from_sqlite_null() -> Result<Self, DrizzleError> {
33 Err(DrizzleError::ConversionError(
34 "unexpected NULL value".into(),
35 ))
36 }
37
38 #[cfg(feature = "rusqlite")]
40 fn from_value_ref(value: ::rusqlite::types::ValueRef<'_>) -> Result<Self, DrizzleError> {
41 match value {
42 ::rusqlite::types::ValueRef::Null => Self::from_sqlite_null(),
43 ::rusqlite::types::ValueRef::Integer(i) => Self::from_sqlite_integer(i),
44 ::rusqlite::types::ValueRef::Real(r) => Self::from_sqlite_real(r),
45 ::rusqlite::types::ValueRef::Text(text) => {
46 let s = std::str::from_utf8(text).map_err(|e| {
47 DrizzleError::ConversionError(format!("invalid UTF-8: {}", e).into())
48 })?;
49 Self::from_sqlite_text(s)
50 }
51 ::rusqlite::types::ValueRef::Blob(blob) => Self::from_sqlite_blob(blob),
52 }
53 }
54}
55
56pub trait DrizzleRow {
61 fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError>;
63
64 fn get_column_by_name<T: FromSQLiteValue>(&self, name: &str) -> Result<T, DrizzleError>;
66}
67
68macro_rules! impl_from_sqlite_value_int {
74 (i64) => {
76 impl FromSQLiteValue for i64 {
77 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
78 Ok(value)
79 }
80
81 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
82 value.parse().map_err(|e| {
83 DrizzleError::ConversionError(format!("cannot parse '{}' as i64: {}", value, e).into())
84 })
85 }
86
87 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
88 Ok(value as i64)
89 }
90
91 fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
92 Err(DrizzleError::ConversionError("cannot convert BLOB to i64".into()))
93 }
94 }
95 };
96 ($($ty:ty),+ $(,)?) => {
98 $(
99 impl FromSQLiteValue for $ty {
100 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
101 value.try_into().map_err(|e| {
102 DrizzleError::ConversionError(
103 format!("i64 {} out of range for {}: {}", value, stringify!($ty), e).into(),
104 )
105 })
106 }
107
108 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
109 value.parse().map_err(|e| {
110 DrizzleError::ConversionError(
111 format!("cannot parse '{}' as {}: {}", value, stringify!($ty), e).into()
112 )
113 })
114 }
115
116 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
117 Ok(value as $ty)
118 }
119
120 fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
121 Err(DrizzleError::ConversionError(
122 concat!("cannot convert BLOB to ", stringify!($ty)).into()
123 ))
124 }
125 }
126 )+
127 };
128}
129
130macro_rules! impl_from_sqlite_value_float {
132 ($($ty:ty),+ $(,)?) => {
133 $(
134 impl FromSQLiteValue for $ty {
135 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
136 Ok(value as $ty)
137 }
138
139 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
140 value.parse().map_err(|e| {
141 DrizzleError::ConversionError(
142 format!("cannot parse '{}' as {}: {}", value, stringify!($ty), e).into()
143 )
144 })
145 }
146
147 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
148 Ok(value as $ty)
149 }
150
151 fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
152 Err(DrizzleError::ConversionError(
153 concat!("cannot convert BLOB to ", stringify!($ty)).into()
154 ))
155 }
156 }
157 )+
158 };
159}
160
161impl_from_sqlite_value_int!(i64);
163impl_from_sqlite_value_int!(i8, i16, i32, isize, u8, u16, u32, u64, usize);
164
165impl_from_sqlite_value_float!(f32, f64);
167
168impl FromSQLiteValue for bool {
169 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
170 Ok(value != 0)
171 }
172
173 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
174 match value.to_lowercase().as_str() {
175 "true" | "1" | "yes" | "on" => Ok(true),
176 "false" | "0" | "no" | "off" => Ok(false),
177 _ => Err(DrizzleError::ConversionError(
178 format!("cannot parse '{}' as bool", value).into(),
179 )),
180 }
181 }
182
183 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
184 Ok(value != 0.0)
185 }
186
187 fn from_sqlite_blob(_value: &[u8]) -> Result<Self, DrizzleError> {
188 Err(DrizzleError::ConversionError(
189 "cannot convert BLOB to bool".into(),
190 ))
191 }
192}
193
194impl FromSQLiteValue for String {
195 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
196 Ok(value.to_string())
197 }
198
199 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
200 Ok(value.to_string())
201 }
202
203 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
204 Ok(value.to_string())
205 }
206
207 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
208 String::from_utf8(value.to_vec()).map_err(|e| {
209 DrizzleError::ConversionError(format!("invalid UTF-8 in BLOB: {}", e).into())
210 })
211 }
212}
213
214impl FromSQLiteValue for Vec<u8> {
215 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
216 Ok(value.to_le_bytes().to_vec())
217 }
218
219 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
220 Ok(value.as_bytes().to_vec())
221 }
222
223 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
224 Ok(value.to_le_bytes().to_vec())
225 }
226
227 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
228 Ok(value.to_vec())
229 }
230}
231
232impl<T: FromSQLiteValue> FromSQLiteValue for Option<T> {
234 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
235 T::from_sqlite_integer(value).map(Some)
236 }
237
238 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
239 T::from_sqlite_text(value).map(Some)
240 }
241
242 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
243 T::from_sqlite_real(value).map(Some)
244 }
245
246 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
247 T::from_sqlite_blob(value).map(Some)
248 }
249
250 fn from_sqlite_null() -> Result<Self, DrizzleError> {
251 Ok(None)
252 }
253}
254
255#[cfg(feature = "rusqlite")]
260impl DrizzleRow for rusqlite::Row<'_> {
261 fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
262 let value_ref = self.get_ref(idx)?;
263 match value_ref {
264 rusqlite::types::ValueRef::Integer(i) => T::from_sqlite_integer(i),
265 rusqlite::types::ValueRef::Text(s) => {
266 let s = std::str::from_utf8(s).map_err(|e| {
267 DrizzleError::ConversionError(format!("invalid UTF-8: {}", e).into())
268 })?;
269 T::from_sqlite_text(s)
270 }
271 rusqlite::types::ValueRef::Real(r) => T::from_sqlite_real(r),
272 rusqlite::types::ValueRef::Blob(b) => T::from_sqlite_blob(b),
273 rusqlite::types::ValueRef::Null => T::from_sqlite_null(),
274 }
275 }
276
277 fn get_column_by_name<T: FromSQLiteValue>(&self, name: &str) -> Result<T, DrizzleError> {
278 let idx = self.as_ref().column_index(name)?;
279 self.get_column(idx)
280 }
281}
282
283#[cfg(feature = "libsql")]
284impl DrizzleRow for libsql::Row {
285 fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
286 let value = self.get_value(idx as i32)?;
287 match value {
288 libsql::Value::Integer(i) => T::from_sqlite_integer(i),
289 libsql::Value::Text(ref s) => T::from_sqlite_text(s),
290 libsql::Value::Real(r) => T::from_sqlite_real(r),
291 libsql::Value::Blob(ref b) => T::from_sqlite_blob(b),
292 libsql::Value::Null => T::from_sqlite_null(),
293 }
294 }
295
296 fn get_column_by_name<T: FromSQLiteValue>(&self, _name: &str) -> Result<T, DrizzleError> {
297 Err(DrizzleError::ConversionError(
299 "libsql does not support column access by name in FromRow".into(),
300 ))
301 }
302}
303
304#[cfg(feature = "turso")]
305impl DrizzleRow for turso::Row {
306 fn get_column<T: FromSQLiteValue>(&self, idx: usize) -> Result<T, DrizzleError> {
307 let value = self.get_value(idx)?;
308 if value.is_null() {
309 T::from_sqlite_null()
310 } else if let Some(&i) = value.as_integer() {
311 T::from_sqlite_integer(i)
312 } else if let Some(s) = value.as_text() {
313 T::from_sqlite_text(s)
314 } else if let Some(&r) = value.as_real() {
315 T::from_sqlite_real(r)
316 } else if let Some(b) = value.as_blob() {
317 T::from_sqlite_blob(b)
318 } else {
319 Err(DrizzleError::ConversionError(
320 "unknown SQLite value type".into(),
321 ))
322 }
323 }
324
325 fn get_column_by_name<T: FromSQLiteValue>(&self, _name: &str) -> Result<T, DrizzleError> {
326 Err(DrizzleError::ConversionError(
327 "turso does not support column access by name in FromRow".into(),
328 ))
329 }
330}
331
332#[cfg(feature = "uuid")]
337impl FromSQLiteValue for uuid::Uuid {
338 fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
339 Err(DrizzleError::ConversionError(
340 "cannot convert INTEGER to UUID".into(),
341 ))
342 }
343
344 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
345 uuid::Uuid::parse_str(value).map_err(|e| {
346 DrizzleError::ConversionError(format!("invalid UUID string '{}': {}", value, e).into())
347 })
348 }
349
350 fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
351 Err(DrizzleError::ConversionError(
352 "cannot convert REAL to UUID".into(),
353 ))
354 }
355
356 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
357 uuid::Uuid::from_slice(value)
358 .map_err(|e| DrizzleError::ConversionError(format!("invalid UUID bytes: {}", e).into()))
359 }
360}
361
362#[cfg(feature = "arrayvec")]
363impl<const N: usize> FromSQLiteValue for arrayvec::ArrayString<N> {
364 fn from_sqlite_integer(value: i64) -> Result<Self, DrizzleError> {
365 let s = value.to_string();
366 arrayvec::ArrayString::from(&s).map_err(|_| {
367 DrizzleError::ConversionError(
368 format!(
369 "String length {} exceeds ArrayString capacity {}",
370 s.len(),
371 N
372 )
373 .into(),
374 )
375 })
376 }
377
378 fn from_sqlite_text(value: &str) -> Result<Self, DrizzleError> {
379 arrayvec::ArrayString::from(value).map_err(|_| {
380 DrizzleError::ConversionError(
381 format!(
382 "Text length {} exceeds ArrayString capacity {}",
383 value.len(),
384 N
385 )
386 .into(),
387 )
388 })
389 }
390
391 fn from_sqlite_real(value: f64) -> Result<Self, DrizzleError> {
392 let s = value.to_string();
393 arrayvec::ArrayString::from(&s).map_err(|_| {
394 DrizzleError::ConversionError(
395 format!(
396 "String length {} exceeds ArrayString capacity {}",
397 s.len(),
398 N
399 )
400 .into(),
401 )
402 })
403 }
404
405 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
406 let s = String::from_utf8(value.to_vec())
407 .map_err(|e| DrizzleError::ConversionError(format!("invalid UTF-8: {}", e).into()))?;
408 arrayvec::ArrayString::from(&s).map_err(|_| {
409 DrizzleError::ConversionError(
410 format!(
411 "String length {} exceeds ArrayString capacity {}",
412 s.len(),
413 N
414 )
415 .into(),
416 )
417 })
418 }
419}
420
421#[cfg(feature = "arrayvec")]
422impl<const N: usize> FromSQLiteValue for arrayvec::ArrayVec<u8, N> {
423 fn from_sqlite_integer(_value: i64) -> Result<Self, DrizzleError> {
424 Err(DrizzleError::ConversionError(
425 "cannot convert INTEGER to ArrayVec<u8>, use BLOB".into(),
426 ))
427 }
428
429 fn from_sqlite_text(_value: &str) -> Result<Self, DrizzleError> {
430 Err(DrizzleError::ConversionError(
431 "cannot convert TEXT to ArrayVec<u8>, use BLOB".into(),
432 ))
433 }
434
435 fn from_sqlite_real(_value: f64) -> Result<Self, DrizzleError> {
436 Err(DrizzleError::ConversionError(
437 "cannot convert REAL to ArrayVec<u8>, use BLOB".into(),
438 ))
439 }
440
441 fn from_sqlite_blob(value: &[u8]) -> Result<Self, DrizzleError> {
442 arrayvec::ArrayVec::try_from(value).map_err(|_| {
443 DrizzleError::ConversionError(
444 format!(
445 "Blob length {} exceeds ArrayVec capacity {}",
446 value.len(),
447 N
448 )
449 .into(),
450 )
451 })
452 }
453}