db-cores 0.1.0

Database core utilities
Documentation
use crate::common::ColumnBaseInfo;
use crate::to_json::MySqlRowParse;
use base64::{engine::general_purpose, Engine};
use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, TimeZone};
use rust_decimal::Decimal;
use serde_json::{json, Value as JsonValue};
use sqlx::mysql::{ MySqlRow,};
use sqlx::{ Column,  Row,  TypeInfo};
use crate::utlis::decode_auto;

pub fn to_json(results: Vec<MySqlRow>) -> anyhow::Result<(Vec<JsonValue>, Vec<ColumnBaseInfo>)> {
    if results.is_empty() {
        return Ok((vec![], vec![]));
    }
    // 1.根据第一行数据生成 hashMap, 确定解析方法
    let first_row = &results[0];
    let MySqlRowParse { methods, columns } = determine_parsing_methods(first_row)?;

    // 2.根据对应col_index,对应的方法, 进行解析;
    let mut data: Vec<JsonValue> = Vec::with_capacity(results.len()); // with_capacity 可以预先分配足够的内存,从而减少内存分配和复制的次数。这样可以提高性能
    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: &MySqlRow) -> anyhow::Result<MySqlRowParse> {
    let columns = row.columns();
    let mut methods: Vec<fn(&MySqlRow, usize) -> JsonValue> = Vec::with_capacity(columns.len());
    let mut new_columns = Vec::with_capacity(columns.len());

    for col in columns {
        let col_index: usize = col.ordinal(); // 获取列索引
        let colum_name = col.name();
        let field_type = col.type_info().name().to_uppercase();

        let method = match field_type.as_str() {
            // 字段类型转大写格式
            "VARCHAR" | "CHAR" | "TEXT" => parse_text_value,

            "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" => parse_text_value,
            // 数字类型
            "INTEGER" | "INT" | "SMALLINT" | "BIGINT" | "TINYINT" | "INT UNSIGNED"
            | "MEDIUMINT" | "SERIAL" => parse_integer_value, // 整数类型

            "DOUBLE" | "FLOAT" | "REAL" => parse_real_value, // 浮点型

            "DECIMAL" | "NUMERIC" => parse_decimal, //  decimal

            // 布尔类型
            "BOOLEAN" | "BOOL" => parse_bool_value, // MySql 中布尔值存储为 TINYINT(1)
            // 日期和时间类型
            "DATETIME" => parse_datetime_value,
            "DATE" => parse_date_value,
            "TIME" => parse_time_value,
            "TIMESTAMP" => parse_timestamp_with_timezone,
            "YEAR" => parse_year_value, // 时间类型转为字符串

            // JSON 类型
            "JSON" => parse_json_value,
            // 二进制类型
            "BLOB" | "TINYBLOB" => parse_blob_or_text, // Blob 转 Base64 编码 有可能是 Blob 或者 Text
            "MEDIUMBLOB" | "LONGBLOB" => parse_blob_value, // Blob 转 Base64 编码
            "BINARY" => parse_blob_value,              //
            "VARBINARY" => parse_varbinary_value,
            "ENUM" | "SET" => parse_text_value, // ENUM 和 SET 也可以解析为字符串
            "BIT" => parse_integer_value,       // 二进制位类型可以解析为整数
            "UNSIGNED BIG INT" => parse_integer_value, // MySql 的无符号大整数

            // 国家字符集类型
            "NCHAR" | "NVARCHAR" | "NATIONAL CHAR" | "NATIONAL VARCHAR" => parse_text_value,
            // 添加空间数据类型
            "GEOMETRY" | "POINT" | "LINESTRING" | "POLYGON" | "MULTIPOINT" | "MULTILINESTRING"
            | "MULTIPOLYGON" | "GEOMETRYCOLLECTION" => parse_geometry_value,

            _ => {
                return Err(anyhow::anyhow!("未知字段类型: {}", col.type_info().name()));
            }
        };
        new_columns.push(ColumnBaseInfo {
            name: colum_name.to_string(),
            r#type: field_type,
            index: col_index as u64,
        });
        methods.push(method);
    }

    Ok(MySqlRowParse {
        methods,
        columns: new_columns,
    })
}

fn parse_value(
    // 解析数据
    row: &MySqlRow,
    col_index: usize,
    parsing_methods: &[fn(&MySqlRow, usize) -> JsonValue],
) -> JsonValue {
    let method = parsing_methods[col_index];
    method(row, col_index)
}

// "DATETIME" | "DATE" | "TIMESTAMP" | "TIME" | "YEAR"
fn parse_text_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    if let Ok(Some(s)) = row.try_get::<Option<String>, _>(col_index) {
        JsonValue::String(s)
    } else if let Ok(Some(b)) = row.try_get::<Option<Vec<u8>>, _>(col_index) {
        JsonValue::String(decode_auto(&b))
    } else {
        JsonValue::Null
    }
}

fn parse_date_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    if let Ok(Some(date)) = row.try_get::<Option<NaiveDate>, _>(col_index) {
        JsonValue::String(date.format("%Y-%m-%d").to_string())
    } else {
        JsonValue::Null
    }
}

fn parse_datetime_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    if let Ok(Some(dt)) = row.try_get::<Option<NaiveDateTime>, _>(col_index) {
        JsonValue::String(dt.format("%Y-%m-%d %H:%M:%S").to_string())
    } else {
        JsonValue::Null
    }
}

fn parse_timestamp_with_timezone(row: &MySqlRow, col_index: usize) -> JsonValue {
    // 获取原始 UTC 时间戳 (秒数)
    if let Ok(Some(ts)) = row.try_get::<Option<i64>, _>(col_index) {
        let utc = DateTime::from_timestamp(ts, 0).unwrap();
        let local = Local.from_utc_datetime(&utc.naive_utc());
        return JsonValue::String(local.format("%Y-%m-%d %H:%M:%S %Z").to_string());
    }
    JsonValue::Null
}

fn parse_year_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    match row.try_get::<Option<i32>, _>(col_index) {
        Ok(Some(year)) => JsonValue::String(year.to_string()),
        Ok(None) | Err(_) => JsonValue::Null,
    }
}

fn parse_time_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    if let Ok(Some(dt)) = row.try_get::<Option<NaiveDateTime>, _>(col_index) {
        JsonValue::String(dt.format("%H:%M:%S").to_string())
    } else {
        JsonValue::Null
    }
}

fn parse_json_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    // 直接从 row 中尝试解析 JSON 值
    match row.try_get::<Option<JsonValue>, _>(col_index) {
        Ok(Some(json_value)) => json_value,
        Ok(None) => JsonValue::Null,
        Err(e) => {
            eprintln!("解析 JSON 出错: {:?}", e);
            JsonValue::Null
        }
    }
}

fn parse_integer_value(row: &MySqlRow, 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_bool_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    // MySql 的布尔值可能是 `bool` 类型、`TINYINT` 类型或 `String` "true"/"false"
    if let Ok(Some(value)) = row.try_get::<Option<bool>, _>(col_index) {
        // 如果是直接的布尔类型
        JsonValue::Bool(value)
    } else if let Ok(Some(value)) = row.try_get::<Option<i8>, _>(col_index) {
        // 如果是 TINYINT 类型(0 或 1)
        JsonValue::Bool(value != 0)
    } else if let Ok(Some(value)) = row.try_get::<Option<String>, _>(col_index) {
        // 如果是字符串 "true" 或 "false"
        match value.to_lowercase().as_str() {
            "true" => JsonValue::Bool(true),
            "false" => JsonValue::Bool(false),
            _ => JsonValue::Null, // 非法值,返回空
        }
    } else {
        // 解析失败或值为 NULL
        JsonValue::Null
    }
}

fn parse_real_value(row: &MySqlRow, 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: &MySqlRow, 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_blob_or_text(row: &MySqlRow, col_index: usize) -> JsonValue {
    match row.try_get::<Option<Vec<u8>>, _>(col_index) {
        Ok(value) => {
            if let Some(i) = value {
                let u = blob_is_text(&i);
                if u {
                    return JsonValue::String(decode_auto(&i));
                }
                JsonValue::String(general_purpose::STANDARD.encode(i))
            } else {
                JsonValue::Null
            }
        }
        Err(_) => JsonValue::Null,
    }
}

fn parse_varbinary_value(row: &MySqlRow, col_index: usize) -> JsonValue {
    match row.try_get::<Option<Vec<u8>>, _>(col_index) {
        Ok(value) => {
            if let Some(i) = value {
                JsonValue::String(decode_auto(&i))
            } else {
                JsonValue::Null
            }
        }
        Err(_) => JsonValue::Null,
    }
}

fn parse_decimal(row: &MySqlRow, col_index: usize) -> serde_json::Value {
    match row.try_get::<Option<Decimal>, _>(col_index) {
        Ok(Some(d)) => serde_json::Value::String(d.to_string()),
        _ => serde_json::Value::Null,
    }
}

fn parse_geometry_value(_row: &MySqlRow, _col_index: usize) -> serde_json::Value {
    serde_json::Value::String("Does not support parsing".to_string())
}
/// 对大数据进行采样检测,判断是否为文本数据
fn blob_is_text(data: &[u8]) -> bool {
    const NON_TEXT_THRESHOLD: f32 = 0.2; // 非文本字符比例的阈值
    const SAMPLE_SIZE: usize = 1024; // 最大采样大小

    let total_len = data.len();
    if total_len == 0 {
        return false; // 空数据直接返回 false
    }

    // 决定采样步长:如果数据量大,用步长采样;否则全量遍历
    let step = if total_len > SAMPLE_SIZE {
        total_len / SAMPLE_SIZE
    } else {
        1 // 如果数据量小,就不跳步
    };

    let mut non_printables = 0; // 非打印字符计数
    let mut checked_count = 0; // 实际检查的字节数

    for (_, &byte) in data.iter().step_by(step).enumerate() {
        checked_count += 1;

        // 检查当前字节是否为非打印字符
        if !byte.is_ascii_graphic() && !byte.is_ascii_whitespace() {
            non_printables += 1;
        }

        // 计算当前采样的非打印字符比例
        let non_printable_ratio = (non_printables as f32) / (checked_count as f32);

        // 如果采样的非打印字符比例超出阈值,提前退出
        if non_printable_ratio >= NON_TEXT_THRESHOLD {
            return false;
        }
    }

    // 如果采样完成且未超出阈值,则认为是文本数据
    true
}

// // 解析时只读取一次窗口函数返回的 count,必须有一列为 count_abc1Ab 是统计数量的列
// pub fn to_json_and_count(results: Vec<MySqlRow>) -> anyhow::Result<ToJsonResult> {
//     if results.is_empty() {
//         return Ok(ToJsonResult {
//             data: vec![],
//             columns: vec![],
//             count: 0,
//         });
//     }
//     // 1.根据第一行数据生成 hashMap, 确定解析方法
//     let first_row = &results[0];
//     let (parsing_methods, columns) = determine_parsing_methods(first_row)?;

//     // 2. 处理总数列
//     let column_names: Vec<&str> = first_row.columns().iter().map(|col| col.name()).collect();
//     let count_column_index = column_names
//         .iter()
//         .position(|&name| name == COUNT_COLUMN_NAME)
//         .unwrap(); // 找到总数字段索引

//     let mut count = 0;
//     if let Ok(value) = first_row.try_get::<i64, _>(count_column_index) {
//         count = value;
//     }

//     // 3.根据对应col_index,对应的方法, 进行解析;
//     let mut data: Vec<JsonValue> = Vec::with_capacity(results.len()); // with_capacity 可以预先分配足够的内存,从而减少内存分配和复制的次数。这样可以提高性能

//     for row in results {
//         let mut row_data = json!({});
//         for col in row.columns() {
//             let col_index: usize = col.ordinal();
//             if col_index == count_column_index {
//                 continue; // 已处理过的总数列忽略
//             }
//             let col_name = col.name();
//             let value = parse_value(&row, col_index, &parsing_methods);
//             row_data[col_name] = value;
//         }
//         data.push(row_data);
//     }
//     Ok(ToJsonResult {
//         data: data,
//         columns: columns,
//         count: count,
//     })
// }