1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
pub mod mssql_mapper;
pub mod mysql_mapper;
pub mod pg_mapper;
pub mod sqlite_mapper;

use crate::executor::Executor;
use crate::Error;
use futures_core::future::BoxFuture;
use log::debug;
pub use mssql_mapper::*;
pub use mysql_mapper::*;
pub use pg_mapper::*;
use rbs::Value;
pub use sqlite_mapper::*;

const PRIMARY_KEY: &'static str = " PRIMARY KEY ";

/// create table if not exists, add column if not exists
/// ```rust
/// use rbatis::executor::{Executor, RBatisConnExecutor};
/// use rbatis::RBatis;
/// use rbatis::table_sync::{MysqlTableMapper, SqliteTableMapper, sync};
/// use rbs::to_value;
///
/// /// let rb = RBatis::new();
/// /// let conn = rb.acquire().await;
/// pub async fn do_sync_table(conn: &dyn Executor){
///     let map = rbs::to_value!{
///             "id":"TEXT",
///             "name":"TEXT",
///      };
///      let _ = sync(conn, &SqliteTableMapper{},map,"user").await;
/// }
///
/// ```
///
/// sync table struct
/// ```rust
/// use rbatis::executor::{Executor, RBatisConnExecutor};
/// use rbatis::RBatis;
/// use rbatis::table_sync::{MysqlTableMapper, SqliteTableMapper, sync};
/// use rbs::to_value;
///
/// #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
/// pub struct User{
///   pub id:String,
///   pub name: Option<String>
/// }
///
/// /// let rb = RBatis::new();
/// /// let conn = rb.acquire().await;
/// pub async fn do_sync_table(conn: &dyn Executor){
///      let table = User{id: "".to_string(), name: Some("".to_string())};
///      let _ = sync(conn, &SqliteTableMapper{},to_value!(table),"user").await;
/// }
///
/// ```
///
/// sync table struct (custom string column type)
/// ```rust
/// use rbatis::executor::Executor;
/// use rbatis::RBatis;
/// use rbatis::table_sync::{MysqlTableMapper, sync};
/// use rbs::to_value;
///
/// #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
/// pub struct User{
///   pub id:String,
///   pub name: Option<String>
/// }
///
/// pub async fn do_sync_table_mysql(conn: &dyn Executor){
///      let table = User{id: "".to_string(), name: Some("VARCHAR(50)".to_string())};
///      let _ = sync(conn, &MysqlTableMapper{},to_value!(table),"user").await;
/// }
/// ```
pub fn sync<'a>(
    executor: &'a dyn Executor,
    mapper: &'a dyn ColumMapper,
    table: Value,
    table_name: &str,
) -> BoxFuture<'a, Result<(), Error>> {
    let name = table_name.to_owned();
    Box::pin(async move {
        match table {
            Value::Map(m) => {
                let mut sql_create = format!("CREATE TABLE {} ", name);
                let mut sql_column = format!("");
                for (k, v) in &m {
                    let k = k.as_str().unwrap_or_default();
                    let column_type = mapper.get_column(k, &v);
                    sql_column.push_str(k);
                    sql_column.push_str(" ");
                    sql_column.push_str(column_type.as_str());
                    if column_type.is_empty() && k.eq("id")
                        || v.as_str().unwrap_or_default() == "id"
                    {
                        sql_column.push_str(&PRIMARY_KEY);
                    }
                    sql_column.push_str(",");
                }
                if sql_column.ends_with(",") {
                    sql_column = sql_column.trim_end_matches(",").to_string();
                }
                sql_create = sql_create + &format!("({});", sql_column);
                let result_create = executor.exec(&sql_create, vec![]).await;
                match result_create {
                    Ok(_) => {}
                    Err(e) => {
                        if e.to_string().to_lowercase().contains("already") {
                            //TODO have any better way do not Repeated add and compatibility with most databases
                            for (k, v) in &m {
                                let k = k.as_str().unwrap_or_default();
                                let mut id_key = "";
                                if k.eq("id") || v.as_str().unwrap_or_default() == "id" {
                                    id_key = &PRIMARY_KEY;
                                }
                                match executor
                                    .exec(
                                        &format!(
                                            "alter table {} add {} {} {};",
                                            name,
                                            k,
                                            mapper.get_column(k, &v),
                                            id_key
                                        ),
                                        vec![],
                                    )
                                    .await
                                {
                                    Ok(_) => {}
                                    Err(e) => {
                                        debug!("ADD COLUMN fail={}", e);
                                        continue;
                                    }
                                }
                            }
                            return Ok(());
                        }
                        return Err(e);
                    }
                }
                Ok(())
            }
            _ => Err(Error::from("table not is an struct or map!")),
        }
    })
}

pub trait ColumMapper: Sync + Send {
    fn get_column(&self, column: &str, v: &Value) -> String;
}