use crate::column_info::ColumnBaseInfo;
use crate::to_json::SqliteRowParse;
use base64::{engine::general_purpose, Engine};
use serde_json::Value as JsonValue;
use sqlx::sqlite::SqliteRow;
use sqlx::{Column, Row, TypeInfo};
use super::f64_to_json_safe;
crate::impl_to_json!(SqliteRow, SqliteRowParse);
fn determine_parsing_methods(
row: &SqliteRow,
) -> anyhow::Result<SqliteRowParse> {
let customizer = super::get_customizer();
let columns = row.columns();
let mut methods: Vec<fn(&SqliteRow, usize) -> JsonValue> = Vec::with_capacity(columns.len());
let mut new_columns = vec![];
for col in columns {
let col_index: usize = col.ordinal();
let col_name = col.name();
let field_type = col.type_info().name().to_uppercase();
let method: fn(&SqliteRow, usize) -> JsonValue = customizer
.customize_sqlite(&field_type)
.map(Ok)
.unwrap_or_else(|| default_sqlite_method(&field_type, col))?;
methods.push(method);
new_columns.push(ColumnBaseInfo {
name: col_name.to_string(),
r#type: field_type,
index: col_index as u64,
});
}
Ok(SqliteRowParse {
methods,
columns: new_columns,
})
}
#[inline]
fn default_sqlite_method(
field_type: &str,
col: &sqlx::sqlite::SqliteColumn,
) -> anyhow::Result<fn(&SqliteRow, usize) -> JsonValue> {
let method = match field_type {
"TEXT" | "TIME" | "DATETIME" | "DATE" => parse_text_value,
"INTEGER" | "BOOLEAN" => parse_integer_value,
"REAL" => parse_real_value,
"BLOB" => parse_blob_value,
"NUMERIC" => parse_numeric_value,
"NULL" => parse_null_value,
"VECTOR" | "FLOAT32" | "FLOAT64" | "INT8" => parse_vector_value,
_ => {
return Err(anyhow::anyhow!(
"SQLite: unknown data type! Column info: {:#?}, column name: {}",
col.type_info(),
col.name(),
))
}
};
Ok(method)
}
fn parse_text_value(row: &SqliteRow, col_index: usize) -> JsonValue {
let text_ref: Option<&str> = row.try_get(col_index).ok().flatten();
match text_ref {
Some(text) => {
if text.starts_with('{') || text.starts_with('[') {
match serde_json::from_str::<JsonValue>(text) {
Ok(parsed) => match parsed {
JsonValue::Array(_) | JsonValue::Object(_) => parsed,
_ => JsonValue::String(text.to_string()),
},
Err(_) => JsonValue::String(text.to_string()),
}
} else {
JsonValue::String(text.to_string())
}
}
None => JsonValue::Null,
}
}
fn parse_integer_value(row: &SqliteRow, col_index: usize) -> JsonValue {
match row.try_get::<Option<i64>, _>(col_index) {
Ok(value) => {
if let Some(i) = value {
JsonValue::Number(i.into())
} else {
JsonValue::Null
}
}
Err(_) => JsonValue::Null,
}
}
fn parse_real_value(row: &SqliteRow, col_index: usize) -> JsonValue {
match row.try_get::<Option<f64>, _>(col_index) {
Ok(Some(f)) => f64_to_json_safe(f),
_ => JsonValue::Null,
}
}
fn parse_blob_value(row: &SqliteRow, col_index: usize) -> JsonValue {
match row.try_get::<Option<Vec<u8>>, _>(col_index) {
Ok(Some(bytes)) => JsonValue::String(general_purpose::STANDARD.encode(bytes)),
_ => JsonValue::Null,
}
}
fn parse_numeric_value(row: &SqliteRow, col_index: usize) -> JsonValue {
row.try_get::<String, _>(col_index)
.map(|v| {
if let Ok(i) = v.parse::<i64>() {
JsonValue::Number(i.into())
} else if let Ok(f) = v.parse::<f64>() {
f64_to_json_safe(f)
} else {
JsonValue::String(v)
}
})
.unwrap_or(JsonValue::Null)
}
fn parse_null_value(row: &SqliteRow, col_index: usize) -> JsonValue {
try_as_i64(row, col_index)
.or_else(|| try_as_string(row, col_index))
.or_else(|| try_as_f64(row, col_index))
.unwrap_or(JsonValue::Null)
}
#[inline]
fn try_as_i64(row: &SqliteRow, col_index: usize) -> Option<JsonValue> {
row.try_get::<Option<i64>, _>(col_index)
.ok()
.flatten()
.map(|i| JsonValue::Number(i.into()))
}
#[inline]
fn try_as_string(row: &SqliteRow, col_index: usize) -> Option<JsonValue> {
row.try_get::<Option<String>, _>(col_index)
.ok()
.flatten()
.map(|text| {
serde_json::from_str::<JsonValue>(&text)
.ok()
.filter(|v| matches!(v, JsonValue::Array(_) | JsonValue::Object(_)))
.unwrap_or_else(|| JsonValue::String(text))
})
}
#[inline]
fn try_as_f64(row: &SqliteRow, col_index: usize) -> Option<JsonValue> {
row.try_get::<Option<f64>, _>(col_index)
.ok()
.flatten()
.map(|f| f64_to_json_safe(f))
}
fn parse_vector_value(row: &SqliteRow, col_index: usize) -> JsonValue {
if let Ok(Some(bytes)) = row.try_get::<Option<Vec<u8>>, _>(col_index) {
return super::parse_vector_bytes(&bytes);
}
if let Ok(Some(s)) = row.try_get::<Option<String>, _>(col_index) {
return super::parse_vector_string(&s);
}
JsonValue::Null
}