use crate::common::ColumnBaseInfo;
use crate::to_json::SqliteRowParse;
use base64::{engine::general_purpose, Engine};
use serde_json::{json, Value as JsonValue};
use sqlx::sqlite::{ SqliteRow};
use sqlx::{ Column, Row, TypeInfo};
pub fn to_json(results: Vec<SqliteRow>) -> anyhow::Result<(Vec<JsonValue>, Vec<ColumnBaseInfo>)> {
if results.is_empty() {
return Ok((vec![], vec![]));
}
let first_row = &results[0];
let SqliteRowParse { methods, columns } = determine_parsing_methods(first_row)?;
let mut data: Vec<JsonValue> = Vec::with_capacity(results.len()); for row in results {
let mut row_data = json!({});
for col in row.columns() {
let col_name = col.name();
let col_index: usize = col.ordinal();
let value = parse_value(&row, col_index, &methods);
row_data[col_name] = value;
}
data.push(row_data);
}
Ok((data, columns))
}
fn determine_parsing_methods(
row: &SqliteRow,
) -> anyhow::Result<SqliteRowParse> {
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 = match field_type.as_str() {
"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, _ => {
return Err(anyhow::anyhow!(
"sqlite未知数据类型! 字段信息: {:#?},字段名:{}",
col.type_info(),
col.name(),
))
}
};
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,
})
}
fn parse_value(
row: &SqliteRow,
col_index: usize,
parsing_methods: &[fn(&SqliteRow, usize) -> JsonValue],
) -> JsonValue {
let method = parsing_methods[col_index];
method(row, col_index)
}
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(value) => {
if let Some(i) = value {
JsonValue::Number(serde_json::Number::from_f64(i).unwrap())
} else {
JsonValue::Null
}
}
Err(_) => JsonValue::Null,
}
}
fn parse_blob_value(row: &SqliteRow, col_index: usize) -> JsonValue {
match row.try_get::<Option<Vec<u8>>, _>(col_index) {
Ok(value) => {
if let Some(i) = value {
JsonValue::String(general_purpose::STANDARD.encode(i))
} else {
JsonValue::Null
}
}
Err(_) => 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>() {
JsonValue::Number(
serde_json::Number::from_f64(f).expect("Failed to create JSON number from f64"),
)
} else {
JsonValue::String(v)
}
})
.unwrap_or(JsonValue::Null)
}
fn parse_null_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(_) => {
match row.try_get::<Option<String>, _>(col_index) {
Ok(optional_text) => {
if let Some(text) = optional_text {
match serde_json::from_str::<JsonValue>(&text) {
Ok(parsed) => match parsed {
JsonValue::Array(_) | JsonValue::Object(_) => parsed,
_ => JsonValue::String(text),
},
Err(_) => JsonValue::String(text),
}
} else {
JsonValue::Null
}
}
Err(_) => match row.try_get::<Option<f64>, _>(col_index) {
Ok(value) => {
if let Some(i) = value {
JsonValue::Number(serde_json::Number::from_f64(i).unwrap())
} else {
JsonValue::Null
}
}
Err(_) => JsonValue::Null,
},
}
}
}
}