db-cores 0.1.0

Database core utilities
Documentation

use serde::{Deserialize, Deserializer, Serialize};
use serde_json::{ Value as JsonValue};
use std::path::PathBuf;
use crate::DatabaseKind;


#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OthersRes<T> {
    pub rows_affected: u64,
    pub sql: Option<String>,
    pub results: Option<Vec<T>>,
}
impl<T> Default for OthersRes<T> {
    fn default() -> Self {
        Self {
            rows_affected: 0,
            results: None,
            sql: None,
        }
    }
}
impl<T> OthersRes<T> {
    pub fn new(rows_affected: u64) -> Self {
        Self {
            rows_affected,
            results: None,
            sql: None,
        }
    }
    pub fn with(data: Vec<T>, rows_affected: usize) -> Self {
        Self {
            rows_affected: rows_affected as u64,
            results: Some(data),
            sql: None,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ColumnBaseInfo {
    pub name: String,
    #[serde(rename = "type")]
    pub r#type: String,
    pub index: u64,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SelectRes<T> {
    pub results: Vec<T>,
    pub length: usize,
    pub count: usize,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sql: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub columns: Option<Vec<ColumnBaseInfo>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub table_name: Option<String>,
}
impl<T> Default for SelectRes<T> {
    fn default() -> Self {
        Self {
            results: Vec::new(),
            length: 0,
            count: 0,
            sql: None,
            columns: None,
            table_name: None,
        }
    }
}
impl<T> SelectRes<T> {
    pub fn new(
        results: Vec<T>,
        count: usize,
        columns: Option<Vec<ColumnBaseInfo>>,
        sql: Option<String>,
    ) -> Self {
        let length = results.len();
        Self {
            results,
            length,
            count,
            sql,
            columns,
            table_name: None,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct SqlRespone {
    pub length: u64,        // 返回的数据长度
    pub rows_affected: u64, // 影响行数
    pub count: u64,
    pub results: Option<Vec<JsonValue>>, // 返回的数据
    pub columns: Option<Vec<ColumnBaseInfo>>,
    pub is_query: bool,
    pub sql: Option<String>,
    pub table_name: Option<String>,
}

#[derive(Debug)]
#[cfg_attr(any(feature = "postgres", feature = "mysql", feature = "sqlite"), derive(sqlx::FromRow))]
pub struct DataCount {
    pub count: u32,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[cfg_attr(any(feature = "postgres", feature = "mysql", feature = "sqlite"), derive(sqlx::FromRow))]
pub struct TableColumn {
    pub name: String,
    pub ordinal_position: i32, // 所有数据库统一使用从 1 开始的字段顺序,sqlite中对应 cid
    #[serde(rename = "type")]
    pub r#type: String,
    pub base_type: Option<String>,          // 原始类型字符串,便于调试
    pub foreign_key_table: Option<String>,  // 外键引用的表
    pub foreign_key_column: Option<String>, // 外键引用的字段

    #[serde(deserialize_with = "bool_or_int_to_bool")]
    pub auto_increment: bool, // 是否自增
    #[serde(deserialize_with = "bool_or_int_to_bool")]
    pub not_null: bool, // 是否非空
    pub dflt_value: Option<String>, // 默认值
    #[serde(deserialize_with = "bool_or_int_to_bool")]
    pub pk: bool, // 是否主键
    pub index_name: Option<String>, // 索引名称
    #[serde(deserialize_with = "bool_or_int_to_bool")]
    pub non_unique: bool, // 是否唯一索引
    pub description: Option<String>, // 字段注释
}

fn bool_or_int_to_bool<'de, D>(deserializer: D) -> std::result::Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    // 尝试反序列化为 JsonValue
    let v = JsonValue::deserialize(deserializer)?;
    // println!("bool_or_int_to_bool:{:#?}",v);
    match v {
        JsonValue::Bool(b) => Ok(b), // 如果是布尔值直接返回
        JsonValue::Number(n) => {
            // 如果是数字,检查是否是 1 或 0
            if let Some(i) = n.as_i64() {
                match i {
                    1 => Ok(true),
                    0 => Ok(false),
                    _ => Err(serde::de::Error::invalid_value(
                        serde::de::Unexpected::Signed(i),
                        &"expected 1 or 0 for boolean value",
                    )),
                }
            } else {
                Err(serde::de::Error::invalid_type(
                    serde::de::Unexpected::Other("non-integer number"),
                    &"expected integer 1 or 0 for boolean value",
                ))
            }
        }
        _ => Err(serde::de::Error::invalid_type(
            serde::de::Unexpected::Other("unexpected value"),
            &"expected boolean or integer 1/0",
        )),
    }
}
// 自定义反序列化函数

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DbConnect {
    pub kind: DatabaseKind,
    #[serde(default = "DbConnect::default_empty")]
    pub host: String,
    pub port: i64,
    #[serde(default = "DbConnect::default_empty")]
    pub username: String,
    #[serde(default = "DbConnect::default_empty")]
    pub password: String,
    #[serde(default = "DbConnect::default_empty")]
    pub db_name: String,
    #[serde(default = "DbConnect::default_empty")]
    pub charset: String,
    pub ssl_mode: Option<String>,
    pub ca_cert: Option<String>,
    pub client_cert: Option<String>,
    pub client_key: Option<String>,
    pub max_connections: Option<i64>,
    pub min_connections: Option<i64>,
    pub timeout: Option<i64>,
    pub service_name: Option<String>,
    pub instance: Option<String>,

    pub file_dir: PathBuf,
    pub file_path: PathBuf,
    pub http: Option<String>,
    pub region: Option<String>,
    pub api: Option<String>,
    pub api_token: Option<String>,
}

impl Default for DbConnect {
    fn default() -> Self {
        Self {
            kind: DatabaseKind::Postgres,
            host: "127.0.0.1".to_string(),
            port: 5432,
            username: "postgres".to_string(),
            password: "postgres".to_string(),
            db_name: "".to_string(),
            charset: "".to_string(),
            ssl_mode: None,
            ca_cert: None,
            client_cert: None,
            client_key: None,
            max_connections: None,
            min_connections: None,
            timeout: None,
            service_name: None,
            instance: None,
            file_dir: PathBuf::new(),
            file_path: PathBuf::new(),
            http: None,
            region: None,
            api: None,
            api_token: None,
        }
    }
}

impl DbConnect {
    fn default_empty() -> String {
        String::new()
    }
    /// 比较并更新,如果有字段更改则返回 true,没有更改返回 false
    pub fn merge_with_changes(&mut self, new_connect: &DbConnect) -> bool {
        let mut changed = false;
        if self.host != new_connect.host {
            self.host = new_connect.host.clone();
            changed = true
        }
        if self.port != new_connect.port {
            self.port = new_connect.port;
            changed = true
        }
        if self.username != new_connect.username {
            self.username = new_connect.username.clone();
            changed = true
        }
        if self.password != new_connect.password {
            self.password = new_connect.password.clone();
            changed = true
        }
        if self.db_name != new_connect.db_name {
            self.db_name = new_connect.db_name.clone();
            changed = true
        }
        if self.charset != new_connect.charset {
            self.charset = new_connect.charset.clone();
            changed = true
        }
        if self.file_path != new_connect.file_path {
            self.file_path = new_connect.file_path.clone();
            changed = true
        }
        if self.file_dir != new_connect.file_dir {
            self.file_dir = new_connect.file_dir.clone();
            changed = true
        }
        if self.api != new_connect.api {
            self.api = new_connect.api.clone();
            changed = true
        }
        if self.http != new_connect.http {
            self.http = new_connect.http.clone();
            changed = true
        }
        // 更新发生,返回 true
        changed
    }
}

#[derive(Serialize, Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct SchemaInfo {
    pub name: String,
    pub tables: Vec<TableInfo>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[cfg_attr(any(feature = "postgres", feature = "mysql", feature = "sqlite"), derive(sqlx::FromRow))]
pub struct DatabaseInfo {
    pub db_name: String,
    pub tables: Vec<TableInfo>,
    pub schemas: Vec<SchemaInfo>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct TableInfo {
    pub table_name: String, // 表名
    #[serde(skip_serializing_if = "Option::is_none")]
    pub table_comment: Option<String>,
    pub columns: Vec<TableColumn>, // 字段列表
    pub create_sql: String,
}