use crate::database::db_interface::{DatabaseInterfaceActions, Query};
use crate::database::errors::DatabaseError;
use crate::env_manager::get_critical_env_var;
use async_trait::async_trait;
use std::sync::{Arc, LazyLock};
use tokio::sync::Mutex;
use tokio_postgres::{Client, NoTls};
static POSTGRESQL_INTERFACE: LazyLock<Mutex<Option<PostgreInterface>>> =
LazyLock::new(|| Mutex::new(Some(PostgreInterface::new())));
pub async fn create_postgre_interface() {
let mut guard = POSTGRESQL_INTERFACE.lock().await;
if guard.is_none() {
*guard = Some(PostgreInterface::new());
}
}
pub async fn get_postgre_interface() -> tokio::sync::MutexGuard<'static, Option<PostgreInterface>> {
POSTGRESQL_INTERFACE.lock().await
}
pub async fn reset_postgre_interface() {
let mut guard = POSTGRESQL_INTERFACE.lock().await;
*guard = Some(PostgreInterface::new());
}
pub struct PostgreInterface {
db_name: String,
db_user: String,
db_password: String,
db_host: String,
db_port: String,
client: Arc<Mutex<Option<Client>>>,
}
impl PostgreInterface {
pub fn new() -> Self {
PostgreInterface {
db_name: get_critical_env_var("DB_NAME"),
db_user: get_critical_env_var("DB_USER"),
db_password: get_critical_env_var("DB_PASSWORD"),
db_host: get_critical_env_var("DB_HOST"),
db_port: get_critical_env_var("DB_PORT"),
client: Arc::new(Mutex::new(None)),
}
}
pub fn get_client(&self) -> Arc<Mutex<Option<Client>>> {
self.client.clone()
}
}
#[async_trait]
impl DatabaseInterfaceActions for PostgreInterface {
async fn connect(&mut self) -> Result<String, DatabaseError> {
let client_ref = self.client.clone();
let config = format!(
"host={} port={} user={} password={} dbname={}",
self.db_host, self.db_port, self.db_user, self.db_password, self.db_name
);
let (client, connection) = tokio_postgres::connect(config.as_str(), NoTls)
.await
.map_err(|e| DatabaseError::ConnectionFailed(format!("Failed to connect: {}", e)))?;
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Connection error: {}", e);
}
});
*client_ref.lock().await = Some(client);
Ok("PostgreSQL Connected".to_string())
}
async fn disconnect(&mut self) -> Result<String, DatabaseError> {
Ok(String::from("PostgreSql Disconnected"))
}
async fn execute_query<Q>(&self, query: Q) -> Result<Q::Output, DatabaseError>
where
Q: Query + Send + 'static,
{
let client_ref = self.get_client();
let locked_client = client_ref.lock().await;
match &*locked_client {
Some(ref client) => query.execute(client).await,
None => Err(DatabaseError::NotInitialized),
}
}
}