use serde::{Serialize,Deserialize}; use std::collections::{HashMap}; use anyhow::{anyhow,Result}; use datastores::defs::DataStore; use crate::redis::RedisPool; use crate::mysql::MysqlPool; use crate::postgres::PostgresPool; use crate::sqlite::SqlitePool; //use crate::tikv::TikvPool; use async_trait::async_trait; //use tkdr::crypt_lib::decrypt; // FIXME fn decrypt(data: &str, _key: &str) -> String { data.to_owned() } #[async_trait] pub trait PoolHandle { async fn connect(&self) -> Self; } #[derive(Debug)] pub enum DataPool { Redis(RedisPool), Mysql(MysqlPool), Postgres(PostgresPool), Sqlite(SqlitePool), //Tikv(TikvPool), File(String), Slab(String), NoPool, } impl PartialEq for DataPool { fn eq(&self, other: &Self) -> bool { match self { DataPool::Redis(source) => match other { DataPool::Redis(target) => source.id == target.id, _ => false, }, DataPool::Mysql(source) => match other { DataPool::Mysql(target) => source.id == target.id, _ => false, }, DataPool::Postgres(source) => match other { DataPool::Postgres(target) => source.id == target.id, _ => false, }, DataPool::Sqlite(source) => match other { DataPool::Sqlite(target) => source.id == target.id, _ => false, }, DataPool::File(source) => match other { DataPool::File(target) => source == target, _ => false, }, DataPool::Slab(source) => match other { DataPool::Slab(target) => source == target, _ => false, }, DataPool::NoPool => match other { DataPool::NoPool => true, _ => false, }, } } } impl Default for DataPool { fn default() -> Self { DataPool::NoPool } } #[derive(Debug, Default)] pub struct AppDataConn { name: String, datastores: HashMap<String,DataPool>, } impl AppDataConn { pub async fn new(name: String, stores_settings: Vec<StoreSettings>, key: &str) -> Self { let mut datastores: HashMap<String,DataPool> = HashMap::new(); for settings in stores_settings.iter() { match settings.datastore { DataStore::Redis => { datastores.insert( settings.id.to_owned(), DataPool::Redis(RedisPool::new(settings.to_owned()).connect_pool().await) ); }, DataStore::Mysql => { datastores.insert( settings.id.to_owned(), DataPool::Mysql(MysqlPool::new(settings.to_owned(),key).await.connect_pool().await) ); }, DataStore::Postgres => { datastores.insert( settings.id.to_owned(), DataPool::Postgres(PostgresPool::new(settings.to_owned(),key).await.connect_pool().await) ); }, DataStore::Sqlite => { datastores.insert( settings.id.to_owned(), DataPool::Sqlite(SqlitePool::new(settings.to_owned()).connect_pool().await) ); }, _ => continue, }; } Self { name, datastores, } } pub fn get_conn(&self,key: &str) -> &DataPool { self.datastores.get(key).unwrap_or(&DataPool::NoPool) } pub async fn get_redis(&self,key: &str) -> Result<&redis::aio::Connection> { match self.datastores.get(key).unwrap_or(&DataPool::NoPool) { DataPool::Redis(redis_conn) => if let Some(pool) = &redis_conn.conn { Ok(pool) } else { Err(anyhow!("Redis pool not available")) }, _ => Err(anyhow!("{} is not a Redis connection",key)), } } pub async fn get_mysql(&self,key: &str) -> Result<&sqlx::pool::PoolConnection<sqlx::MySql>> { match self.datastores.get(key).unwrap_or(&DataPool::NoPool) { DataPool::Mysql(mysql_conn) => if let Some(pool) = &mysql_conn.conn { Ok(pool) } else { Err(anyhow!("Mysql pool not available")) }, _ => Err(anyhow!("{} is not a Mysql connection",key)), } } pub async fn get_postgres(&self,key: &str) -> Result<&sqlx::pool::PoolConnection<sqlx::Postgres>> { match self.datastores.get(key).unwrap_or(&DataPool::NoPool) { DataPool::Postgres(postgres_conn) => if let Some(pool) = &postgres_conn.conn { Ok(pool) } else { Err(anyhow!("Postgres pool not available")) }, _ => Err(anyhow!("{} is not a Postgres connection",key)), } } pub async fn get_sqlite(&self,key: &str) -> Result<&sqlx::Pool<sqlx::Sqlite>> { match self.datastores.get(key).unwrap_or(&DataPool::NoPool) { DataPool::Sqlite(sqlite_conn) => if let Some(pool) = &sqlite_conn.conn { Ok(pool) } else { Err(anyhow!("Sqlite pool not available")) }, _ => Err(anyhow!("{} is not a Sqlite connection",key)), } } pub async fn check_connections(&self, datastores_settings: Vec<StoreSettings>) -> bool { let debug = envmnt::get_isize("DEBUG",0); let mut status = false; for con in &datastores_settings { match con.datastore { DataStore::Redis => { status = match self.get_redis(&con.id).await { Ok(_pool) => { if debug > 0 { println!("app_data_conn found redis pool"); } true }, Err(e) => { if StoreSettings::check_required_id(datastores_settings.to_owned(),&con.id) { panic!("Error app_data_conn required: {}",e); } else { println!("Error app_data_conn: {}",e); } false }, } } _ => { continue; } }; } status } } #[derive(Clone, Debug, Serialize, Deserialize, Default)] pub struct StoreSettings { pub id: String, pub host: String, pub port: u32, pub user: String, pub pass: String, pub datastore: DataStore, pub database: String, pub prefix: String, pub max_conn: u32, pub required: bool, } impl StoreSettings { pub fn get_credentials(&self,key: &str) -> (String,String) { let user: String; let pass: String; if key.is_empty() { user = self.user.to_owned(); pass = self.pass.to_owned(); } else { user = decrypt(&self.user, key); pass = decrypt(&self.pass, key); } (user,pass) } pub fn url_db(&self, key: &str) -> String { let store = self.get_store(); let (user,pass) = self.get_credentials(key); let user_pass: String; if ! user.is_empty() || ! pass.is_empty() { user_pass = format!("{}:{}", &user, &pass); } else { user_pass = String::from(""); } let host = &self.host; let port = &self.port; let database = &self.database; format!( "{}://{}@{}:{}/{}", store, &user_pass, &host, &port, &database ) } pub fn url_keyval(&self) -> String { let store = self.get_store(); let host = &self.host; let port = &self.port; format!("{}://{}:{}", store, &host, &port) } pub fn url_local(&self) -> String { let store = self.get_store(); format!("{}.{}", store, &self.database) } pub fn get_store(&self) -> String { match self.datastore { DataStore::File => String::from("file"), DataStore::Mysql => String::from("mysql"), DataStore::Postgres => String::from("postgres"), DataStore::Sqlite => String::from("sqlite"), DataStore::Redis => String::from("redis"), // DataStore::Tikv => String::from("tikv"), DataStore::Slab => String::from("slab"), DataStore::Unknown => String::from("Unknown"), } } pub fn find_storesetting_id(cfg_store_settings: Vec<StoreSettings>, id: &str) -> Result<Vec<StoreSettings>> { let datastore_found: Vec<StoreSettings> = cfg_store_settings.iter().filter(|itm| itm.id == id).cloned().collect(); if datastore_found.len() > 0 { Ok(datastore_found) } else { Err(anyhow!("No DataStore Settings found for: {}",&id)) } } pub fn check_required_id(cfg_store_settings: Vec<StoreSettings>, id: &str) -> bool { match StoreSettings::find_storesetting_id(cfg_store_settings.to_owned(),id) { Ok(ds_found) => if let Some(item) = ds_found.get(0) { item.required } else { false }, Err(e) => { eprintln!("Error check required: {}",e); false }, } } } #[derive(Default)] pub struct DataStorePool<T> where T : PoolHandle + Default { pub pool: T, pub typ: DataStore, }