data_goes 0.1.0-alpha.2

Biblioteca experimental para demonstração.
Documentation
use crate::map_schema::{Column, Table, Schema, Database, Root, Server};
use tokio::sync::Mutex;
pub use crate::config::ConfigClient;
use crate::generator_builder::GeneratorBuilder;
use mssql_client::{Client, Config, Ready};
use anyhow::{Ok, Result};
const SCHEMA: &str =include_str!("../querys/sql_schema.sql");
pub struct Generator{   
    client:Mutex<Client<Ready>>,
    host:String,
}


impl Generator {
    pub async fn new<'a>(cfg: &ConfigClient<'a>)->Result<Self>{
        let connect =format!("Server={};User Id={};Password={};TrustServerCertificate=true", cfg.host, cfg.user, cfg.password);
        let config= Config::from_connection_string(&connect)?;
        let client=Client::connect(config).await?;
        Ok(Self{client: Mutex::new(client),
                host: cfg.host.to_string(),})
    }
    pub fn builder<'a>() -> GeneratorBuilder<'a> {
        GeneratorBuilder::new()
    }
    pub async fn get_schema(&self)->Result<Root>{
        let mut client = self.client.lock().await;
        let rows=client.query(SCHEMA, &[]).await?;
        let mut root = Root{
            servers:std::collections::BTreeMap::new(),
        };
        for result in rows{
            let row= result?;
            let db: String= row.get(0)?;
            let schema_name: String= row.get(1)?;
            let table_name: String= row.get(2)?;

            let column=Column{
                name:row.get(3)?,
                data_type:row.get(4)?,
            };
            let server = root.servers.entry(self.host.clone()).or_insert(Server {databases: std::collections::BTreeMap::new(),});

            let database=server.databases.entry(db).or_insert(Database { schemas: std::collections::BTreeMap::new(), });

            let schema = database.schemas.entry(schema_name).or_insert(Schema { tables: std::collections::BTreeMap::new(), });

            let table = schema.tables.entry(table_name).or_insert(Table { columns: Vec::new(), });

            table.columns.push(column);
        }
        let toml = toml::to_string_pretty(&root)?;
        std::fs::write("schema.toml", toml)?;
        Ok(root)
    }
    pub fn code_generator(&self, root: &Root)->String{
        let mut code = String::new();

        code.push_str(r#" // --- NÃO ALTERE ESTE DOCUMENTO MANUALMENTE ---
        use std::marker::PhantomData;

pub struct Column<T> {
    pub nome: &'static str,
    pub _tipo: PhantomData<T>,
}

pub trait ClientSqlExt {
        "#
        );
        let mut count_table=std::collections::HashMap::new();
        for server in root.servers.values(){
            for database in server.databases.values(){
                for schema in database.schemas.values(){
                    for table in  schema.tables.keys(){
                        *count_table.entry(table.clone()).or_insert(0) +=1;
                    }
                }
            }
        }

        let mut processed_tables=std::collections::HashSet::new();
        for server in root.servers.values(){
            for database in server.databases.values(){
                for schema in database.schemas.values(){
                    for table in schema.tables.keys(){
                        if processed_tables.insert(table.clone()){
                            let count = count_table.get(table).unwrap_or(&0);
                            let mut chars = table.chars();
                            let struct_name= match chars.next(){
                                None => String::new(),
                                Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
                            };
                            if *count == 1 {
                                code.push_str(&format!(
                                    "    fn {}(&self) -> Table{} {{ Table{} }}\n",
                                    table.to_ascii_lowercase(), struct_name, struct_name
                                ));
                            }
                        }
                    }
                }
            }
        }
        code.push_str("}\n\nimpl ClientSqlExt for crate::ClientSql {}\n\n");
        for server in root.servers.values(){
            for database in server.databases.values(){
                for schema in database.schemas.values(){
                    for(table_name, table ) in &schema.tables{
                        let mut chars = table_name.chars();
                        let struct_name= match chars.next(){
                                None => String::new(),
                                Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
                            };
                        let name_field = format!("{}Field", struct_name);
                        let name_table= format!("Table{}", struct_name);
                        code.push_str(&format!("pub struct {} {{\n", name_field));
                        for column in &table.columns{
                            let rust_type = Self::type_translator(&column.data_type);
                            code.push_str(&format!(" pub {}: Column<{}>,\n", column.name, rust_type));
                        }
                        code.push_str("}\n\n");
                        code.push_str(&format!("pub struct {};\n\n", name_table));
                        code.push_str(&format!("impl {} {{\n", name_table));
                        code.push_str(&format!(
                            "    pub fn select<F, Retorno>(self, f: F)\n    where F: FnOnce({}) -> Retorno\n    {{\n",
                        name_field
                        ));
                        code.push_str(&format!("        let fields = {} {{\n", name_field));

                    for col in &table.columns {
                        code.push_str(&format!(
                            "            {}: Column {{ nome: \"{}\", _tipo: PhantomData }},\n",
                            col.name, col.name
                        ));
                    }
                    code.push_str("        };\n");
                    code.push_str("        let _selecao = f(fields);\n");
                    code.push_str("    }\n");
                    code.push_str("}\n\n");
                    }
                }
            }
        }
        code
    }

    pub fn type_translator(sql_type: &str)->&'static str{
        let clean_type=sql_type.to_uppercase();
        let base = clean_type.split('(').next().unwrap_or("").trim();
        match base {
            "INT" | "INTEGER" => "Option<i32>",
            "BIGINT" => "Option<i64>",
            "SMALLINT" => "Option<i16>",
            "TINYINT" => "Option<u8>",
            "BIT" => "Option<bool>",
            "DECIMAL" | "NUMERIC" | "MONEY" => "Option<rust_decimal::Decimal>",
            "FLOAT" => "Option<f64>",
            "REAL" => "Option<f32>",
            "CHAR" | "NCHAR" | "VARCHAR" | "NVARCHAR" | "TEXT" | "NTEXT" | "XML" => "Option<String>",
            "DATE" => "Option<chrono::NaiveDate>",
            "TIME" => "Option<chrono::NaiveTime>",
            "DATETIME" | "DATETIME2" | "SMALLDATETIME" => "Option<chrono::NaiveDateTime>",
            "DATETIMEOFFSET" => "Option<chrono::DateTime<chrono::FixedOffset>>",
            "UNIQUEIDENTIFIER" => "Option<uuid::Uuid>",
            "VARBINARY" | "IMAGE" | "TIMESTAMP" => "Option<Vec<u8>>",
            _ => "Option<String>",
        }
    }
}