1use sqlx::sqlite::SqliteRow;
4
5use crate::prelude::*;
6
7impl RowExt for SqliteRow {
8 fn to_json(&self) -> Value {
9 let columns = self.columns();
10 let mut map = Map::with_capacity(columns.len());
11
12 for column in columns {
13 let idx = column.ordinal();
14 let type_name = column.type_info().name();
15
16 let value = if self.try_get_raw(idx).is_ok_and(|v| v.is_null()) {
17 Value::Null
18 } else {
19 match type_name {
20 "BOOLEAN" | "BOOL" => self.try_get::<bool, _>(idx).map_or(Value::Null, Value::Bool),
21
22 "INTEGER" | "INT" | "BIGINT" | "SMALLINT" | "TINYINT" | "MEDIUMINT" => self
23 .try_get::<i64, _>(idx)
24 .map_or(Value::Null, |v| Value::Number(v.into())),
25
26 "REAL" | "FLOAT" | "DOUBLE" | "NUMERIC" => self
27 .try_get::<f64, _>(idx)
28 .ok()
29 .and_then(serde_json::Number::from_f64)
30 .map_or(Value::Null, Value::Number),
31
32 "BLOB" => self
33 .try_get::<Vec<u8>, _>(idx)
34 .map_or(Value::Null, |bytes| Value::String(BASE64.encode(&bytes))),
35
36 "TEXT" | "VARCHAR" | "CHAR" | "CLOB" | "DATE" | "DATETIME" | "TIMESTAMP" | "TIME" => {
37 self.try_get::<String, _>(idx).map_or(Value::Null, Value::String)
38 }
39
40 _ => dynamic_probe(self, idx),
43 }
44 };
45
46 map.insert(column.name().to_string(), value);
47 }
48
49 Value::Object(map)
50 }
51}
52
53fn dynamic_probe(row: &SqliteRow, idx: usize) -> Value {
55 if let Ok(n) = row.try_get::<i64, _>(idx) {
56 return Value::Number(n.into());
57 }
58 if let Ok(f) = row.try_get::<f64, _>(idx) {
59 return serde_json::Number::from_f64(f).map_or(Value::Null, Value::Number);
60 }
61 row.try_get::<String, _>(idx).map_or(Value::Null, Value::String)
62}