use async_std::net::TcpStream;
use serde::Deserialize;
use tiberius::{AuthMethod, Config};
use tokio_postgres::{Client, NoTls};
use crate::datasources::DatasourceProperties;
#[derive(Deserialize, Debug, Eq, PartialEq, Clone, Copy, Default)]
pub enum DatabaseType {
#[default]
#[serde(alias = "postgres", alias = "postgresql")]
PostgreSql,
#[serde(alias = "sqlserver", alias = "mssql")]
SqlServer,
}
pub struct PostgreSqlConnection {
pub client: Client,
}
pub struct SqlServerConnection {
pub client: &'static mut tiberius::Client<TcpStream>,
}
pub struct DatabaseConnection {
pub postgres_connection: Option<PostgreSqlConnection>,
pub sqlserver_connection: Option<SqlServerConnection>,
pub database_type: DatabaseType,
}
unsafe impl Send for DatabaseConnection {}
unsafe impl Sync for DatabaseConnection {}
impl DatabaseConnection {
pub async fn new(
datasource: &DatasourceProperties<'_>,
) -> Result<DatabaseConnection, Box<(dyn std::error::Error + Send + Sync + 'static)>> {
match datasource.db_type {
DatabaseType::PostgreSql => {
let (new_client, new_connection) = tokio_postgres::connect(
&format!(
"postgres://{user}:{pswd}@{host}:{port}/{db}",
user = datasource.username,
pswd = datasource.password,
host = datasource.host,
port = datasource.port.unwrap_or_default(),
db = datasource.db_name
)[..],
NoTls,
)
.await?;
tokio::spawn(async move {
if let Err(e) = new_connection.await {
eprintln!("An error occured while trying to connect to the PostgreSQL database: {e}");
}
});
Ok(Self {
postgres_connection: Some(PostgreSqlConnection {
client: new_client,
}),
sqlserver_connection: None,
database_type: DatabaseType::PostgreSql,
})
}
DatabaseType::SqlServer => {
let mut config = Config::new();
config.host(datasource.host);
config.port(datasource.port.unwrap_or_default());
config.database(datasource.db_name);
config.authentication(AuthMethod::sql_server(
datasource.username,
datasource.password,
));
config.trust_cert();
let tcp = TcpStream::connect(config.get_addr())
.await
.expect("Error instanciating the SqlServer TCP Stream");
tcp.set_nodelay(true)
.expect("Error in the SqlServer `nodelay` config");
let client = tiberius::Client::connect(config, tcp).await;
Ok(Self {
postgres_connection: None,
sqlserver_connection: Some(SqlServerConnection {
client: Box::leak(Box::new(
client.expect("A failure happened connecting to the database"),
)),
}),
database_type: DatabaseType::SqlServer,
})
}
}
}
}
#[cfg(test)]
mod database_connection_handler {
use super::*;
use crate::CanyonSqlConfig;
const CONFIG_FILE_MOCK_ALT: &str = r#"
[canyon_sql]
datasources = [
{name = 'PostgresDS', properties.db_type = 'postgresql', properties.username = 'username', properties.password = 'random_pass', properties.host = 'localhost', properties.db_name = 'triforce', properties.migrations='enabled'},
{name = 'SqlServerDS', properties.db_type = 'sqlserver', properties.username = 'username2', properties.password = 'random_pass2', properties.host = '192.168.0.250.1', properties.port = 3340, properties.db_name = 'triforce2', properties.migrations='disabled'}
]
"#;
#[test]
fn check_from_datasource() {
let config: CanyonSqlConfig = toml::from_str(CONFIG_FILE_MOCK_ALT)
.expect("A failure happened retrieving the [canyon_sql] section");
let psql_ds = &config.canyon_sql.datasources[0].properties;
let sqls_ds = &config.canyon_sql.datasources[1].properties;
assert_eq!(psql_ds.db_type, DatabaseType::PostgreSql);
assert_eq!(sqls_ds.db_type, DatabaseType::SqlServer);
}
}