1use crate::game::errors::database::GameDatabaseError;
2use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
3use diesel::PgConnection;
4use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
5use std::sync::{Arc, RwLock};
6
7pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
8
9pub trait DatabaseInterface: Send + Sync {
10 fn connect(&mut self, postgres_url: &str) -> Result<(), GameDatabaseError>;
11 fn run_migrations(&self) -> Result<(), GameDatabaseError>;
12 fn get_connection(
13 &self,
14 ) -> Result<PooledConnection<ConnectionManager<PgConnection>>, GameDatabaseError>;
15 fn clear(&self) -> Result<(), GameDatabaseError>;
16}
17
18pub struct Database {
19 connection_pool: Option<Pool<ConnectionManager<PgConnection>>>,
20}
21
22impl Database {
23 pub fn new() -> Self {
24 Self {
25 connection_pool: None,
26 }
27 }
28
29 pub fn create() -> Arc<RwLock<dyn DatabaseInterface>> {
30 Arc::new(RwLock::new(Self::new()))
31 }
32}
33
34impl DatabaseInterface for Database {
35 fn connect(&mut self, postgres_url: &str) -> Result<(), GameDatabaseError> {
36 let connection_manager = ConnectionManager::<PgConnection>::new(postgres_url);
37 let pool = Pool::builder()
38 .build(connection_manager)
39 .map_err(|e| GameDatabaseError::connection_failed(&e.to_string()))?;
40 self.connection_pool = Some(pool);
41 self.run_migrations()?;
42 Ok(())
43 }
44
45 fn run_migrations(&self) -> Result<(), GameDatabaseError> {
46 let mut connection = self.get_connection()?;
47 connection
48 .run_pending_migrations(MIGRATIONS)
49 .map_err(|e| GameDatabaseError::migrations_failed(&e.to_string()))?;
50 Ok(())
51 }
52
53 fn get_connection(
54 &self,
55 ) -> Result<PooledConnection<ConnectionManager<PgConnection>>, GameDatabaseError> {
56 match &self.connection_pool {
57 Some(pool) => pool
58 .get()
59 .map_err(|e| GameDatabaseError::connection_failed(&e.to_string())),
60 None => Err(GameDatabaseError::missing_connection()),
61 }
62 }
63
64 fn clear(&self) -> Result<(), GameDatabaseError> {
65 let mut connection = self.get_connection()?;
66
67 connection
68 .revert_all_migrations(MIGRATIONS)
69 .map_err(|e| GameDatabaseError::migrations_failed(&e.to_string()))?;
70
71 connection
72 .run_pending_migrations(MIGRATIONS)
73 .map_err(|e| GameDatabaseError::migrations_failed(&e.to_string()))?;
74
75 Ok(())
76 }
77}