use std::{
ops::Deref,
sync::{Arc, LazyLock, RwLock},
};
use crate::Database;
#[allow(clippy::type_complexity)]
static DATABASE: LazyLock<Arc<RwLock<Option<Arc<Box<dyn Database>>>>>> =
LazyLock::new(|| Arc::new(RwLock::new(None)));
pub fn init(database: Arc<Box<dyn Database>>) {
*DATABASE.write().unwrap() = Some(database);
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone)]
pub struct ConfigDatabase {
pub database: Arc<Box<dyn Database>>,
}
impl From<&ConfigDatabase> for Arc<Box<dyn Database>> {
fn from(value: &ConfigDatabase) -> Self {
value.database.clone()
}
}
impl From<ConfigDatabase> for Arc<Box<dyn Database>> {
fn from(value: ConfigDatabase) -> Self {
value.database
}
}
impl From<Arc<Box<dyn Database>>> for ConfigDatabase {
fn from(value: Arc<Box<dyn Database>>) -> Self {
Self { database: value }
}
}
impl<'a> From<&'a ConfigDatabase> for &'a dyn Database {
fn from(value: &'a ConfigDatabase) -> Self {
&**value.database
}
}
impl Deref for ConfigDatabase {
type Target = dyn Database;
fn deref(&self) -> &Self::Target {
&**self.database
}
}
#[cfg(feature = "api")]
mod api {
use actix_web::{FromRequest, HttpRequest, dev::Payload, error::ErrorInternalServerError};
use futures::future::{Ready, err, ok};
use super::DATABASE;
impl FromRequest for super::ConfigDatabase {
type Error = actix_web::Error;
type Future = Ready<Result<Self, actix_web::Error>>;
fn from_request(_req: &HttpRequest, _: &mut Payload) -> Self::Future {
let Some(database) = DATABASE.read().unwrap().clone() else {
return err(ErrorInternalServerError("Config database not initialized"));
};
ok(Self { database })
}
}
}
#[cfg(all(test, feature = "simulator"))]
mod tests {
use super::*;
use crate::simulator::SimulationDatabase;
#[test_log::test]
fn test_config_database_from_arc() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
let config_db: ConfigDatabase = db.clone().into();
assert!(std::ptr::addr_eq(
std::ptr::addr_of!(**config_db.database),
std::ptr::addr_of!(**db)
));
}
#[test_log::test]
fn test_config_database_from_ref() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
let config_db = ConfigDatabase {
database: db.clone(),
};
let arc_db: Arc<Box<dyn Database>> = (&config_db).into();
assert!(std::ptr::addr_eq(
std::ptr::addr_of!(**arc_db),
std::ptr::addr_of!(**db)
));
}
#[test_log::test]
fn test_config_database_into_arc() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
let config_db = ConfigDatabase {
database: db.clone(),
};
let arc_db: Arc<Box<dyn Database>> = config_db.into();
assert!(std::ptr::addr_eq(
std::ptr::addr_of!(**arc_db),
std::ptr::addr_of!(**db)
));
}
#[test_log::test]
fn test_config_database_deref() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
let config_db = ConfigDatabase {
database: db.clone(),
};
let _db_ref: &dyn Database = &*config_db;
}
#[test_log::test]
fn test_config_database_ref_into_dyn_database() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
let config_db = ConfigDatabase { database: db };
let db_ref: &dyn Database = (&config_db).into();
let _ = db_ref;
}
#[test_log::test]
fn test_init_and_reuse() {
let db = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
init(db);
let db2 = Arc::new(
Box::new(SimulationDatabase::new_for_path(None).unwrap()) as Box<dyn Database>
);
init(db2);
}
}