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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use r2d2::Pool;
use r2d2_postgres::{PostgresConnectionManager};
use r2d2::Config;
use postgres::SslMode;
use config::DbConfig;
use database::{Database, DatabaseDDL, DatabaseDev};
use platform::Postgres;
#[cfg(feature = "sqlite")]
use platform::Sqlite;
use platform::Mysql;
use mysql::conn::pool::{MyPool};
use mysql::conn::MyOpts;
use database::DbError;

#[cfg(feature = "sqlite")]
use r2d2_sqlite::SqliteConnectionManager;


/// the sql builder for each of the database platform
pub enum Platform{
    Postgres(Postgres),
    #[cfg(feature = "sqlite")]
    Sqlite(Sqlite),
    Oracle,
    Mysql(Mysql),
}


impl Platform{
    
    pub fn as_ref(&self)->&Database{
        match *self{
            Platform::Postgres(ref pg) => pg,
            #[cfg(feature = "sqlite")]
            Platform::Sqlite(ref lite) => lite,
             Platform::Mysql(ref my) => my,
            _ => panic!("others not yet..")
        }
    }
    pub fn as_ddl(&self)->&DatabaseDDL{
        match *self{
            Platform::Postgres(ref pg) => pg,
            #[cfg(feature = "sqlite")]
            Platform::Sqlite(ref lite) => lite,
            Platform::Mysql(ref my) => my,
            _ => panic!("others not yet..")
        }
    }
    
    pub fn as_dev(&self)->&DatabaseDev{
        match *self{
            Platform::Postgres(ref pg) => pg,
            #[cfg(feature = "sqlite")]
            Platform::Sqlite(ref lite) => lite,
            _ => panic!("others not yet..")
        }
    }
}

/// Postgres, Sqlite uses r2d2 connection manager,
/// Mysql has its own connection pooling
pub enum ManagedPool{
    Postgres(Pool<PostgresConnectionManager>),
    #[cfg(feature = "sqlite")]
    Sqlite(Pool<SqliteConnectionManager>),
    Oracle,
    Mysql(Option<MyPool>),
}

impl ManagedPool{
    
    /// initialize the pool
    pub fn init(url: &str, pool_size: usize)->Result<Self, DbError>{
        let config = DbConfig::from_url(url);
        match config{
            Some(config) => {
                let platform:&str = &config.platform;
                match platform{
                    "postgres" => {
                        let manager = PostgresConnectionManager::new(url, SslMode::None).unwrap();
                        let config = Config::builder().pool_size(pool_size as u32).build();
                        let pool = Pool::new(config, manager);
                        match pool{
                            Ok(pool) => Ok(ManagedPool::Postgres(pool)),
                            Err(e) => {
                                println!("Unable to create a pool");
                                Err(DbError::new(&format!("{}",e)))
                            }
                        }
                        
                    }
                    #[cfg(feature = "sqlite")]
                    "sqlite" => {
                        let manager = SqliteConnectionManager::new(&config.database).unwrap();
                        let config = Config::builder().pool_size(pool_size as u32).build();
                        match Pool::new(config, manager){
                            Ok(pool) => Ok(ManagedPool::Sqlite(pool)),
                            Err(e) => {
                                println!("Unable to create a pool");
                                Err(DbError::new(&format!("{}",e)))
                            }
                        }
                        
                    }
                    "mysql" => {
                        let opts = MyOpts {
                            user: config.username,
                            pass: config.password,
                            db_name: Some(config.database),
                            tcp_addr: Some(config.host.unwrap().to_string()),
                            tcp_port: config.port.unwrap_or(3306),
                            ..Default::default()
                        };
                        match MyPool::new_manual(0, pool_size, opts){
                            Ok(pool) => Ok(ManagedPool::Mysql(Some(pool))),
                            Err(e) => {
                                println!("Unable to create a pool");
                                Err(DbError::new(&format!("{}",e)))
                            }
                        }
                        
                    }
                    _ => unimplemented!()
                }
            },
            None => {
                println!("Unable to parse url");
                Err(DbError::new("Error parsing url"))
            }
        }
        
    }
    
    /// a conection is created here
    pub fn connect(&self)->Result<Platform, DbError>{
        match *self{
            ManagedPool::Postgres(ref pool) => {
                match pool.get(){
                    Ok(conn) => {
                        let pg = Postgres::with_pooled_connection(conn);
                        Ok(Platform::Postgres(pg))
                    },
                    Err(e) => {
                        Err(DbError::new(&format!("Unable to connect due to {}", e)))
                    }
                }
            },
            #[cfg(feature = "sqlite")]
            ManagedPool::Sqlite(ref pool) => {
                match pool.get(){
                    Ok(conn) => {
                        let lite = Sqlite::with_pooled_connection(conn);
                        Ok(Platform::Sqlite(lite))
                    },
                    Err(e) => {
                        Err(DbError::new(&format!("Unable to connect due to {}", e)))
                    }
                }
            },
            ManagedPool::Mysql(ref pool) => {
                let my = Mysql::with_pooled_connection(pool.clone().unwrap());// I hope cloning doesn't really clone the pool, just the Arc
                Ok(Platform::Mysql(my))
            },
            _ => Err(DbError::new("Any other database is not yet supported"))
        }
    }
}