fish_lib/
database.rs

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}