forked from torrust/torrust-tracker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.rs
More file actions
127 lines (119 loc) · 5.23 KB
/
Copy pathsetup.rs
File metadata and controls
127 lines (119 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! This module provides functionality for setting up databases.
//!
//! For the persistence trait boundary and wiring rationale, see ADR
//! [`20260429000000_keep_database_as_aggregate_supertrait`](../../../docs/adrs/20260429000000_keep_database_as_aggregate_supertrait.md).
use std::sync::Arc;
use torrust_tracker_configuration::Core;
use super::driver::Driver;
use super::driver::mysql::Mysql;
use super::driver::postgres::Postgres;
use super::driver::sqlite::Sqlite;
use super::traits::{AuthKeyStore, SchemaMigrator, TorrentMetricsStore, WhitelistStore};
/// A bundle of narrow-trait store references, one per persistence context.
///
/// The factory (`initialize_database`) constructs the concrete driver once and
/// coerces it into each narrow `Arc<dyn XxxStore>`. Individual services are
/// wired at construction time by passing the relevant field
/// (e.g. `database_stores.auth_key_store.clone()`) to each constructor.
/// Services themselves never hold a `DatabaseStores`; they only see the narrow
/// trait they need.
pub struct DatabaseStores {
/// Schema lifecycle: create / drop tables.
pub schema_migrator: Arc<dyn SchemaMigrator>,
/// Per-torrent and global download counters.
pub torrent_metrics_store: Arc<dyn TorrentMetricsStore>,
/// Torrent infohash whitelist.
pub whitelist_store: Arc<dyn WhitelistStore>,
/// Authentication key persistence.
pub auth_key_store: Arc<dyn AuthKeyStore>,
}
fn build_database_stores<T>(db: Arc<T>) -> DatabaseStores
where
T: SchemaMigrator + TorrentMetricsStore + WhitelistStore + AuthKeyStore + Send + Sync + 'static,
{
DatabaseStores {
schema_migrator: db.clone(),
torrent_metrics_store: db.clone(),
whitelist_store: db.clone(),
auth_key_store: db,
}
}
/// Initializes and returns a [`DatabaseStores`] bundle based on the provided
/// configuration.
///
/// This function creates a new database driver according to the settings
/// defined in the [`Core`] configuration. It selects the appropriate driver
/// (either `Sqlite3` or `MySQL`) as specified in `config.database.driver` and
/// attempts to build the database connection using the path defined in
/// `config.database.path`.
///
/// The concrete driver is constructed once and coerced into four narrow
/// `Arc<dyn XxxStore>` references, one for each persistence context.
///
/// # Panics
///
/// This function will panic if the database cannot be initialized (i.e., if the
/// driver fails to build the connection). This is enforced by the use of
/// [`expect`](std::result::Result::expect) in the implementation.
///
/// In particular, schema initialization issues a query against the configured
/// database immediately after the driver is built. If the database service is
/// not yet ready to accept connections (for example, a freshly started `MySQL`
/// container that has not finished binding its TCP listener), the first query
/// can fail and this function will panic. The `sqlx` driver does not retry the
/// initial connection on its own, so callers are responsible for ensuring the
/// database is reachable before calling `initialize_database`.
///
/// Other panic causes include malformed connection URLs, authentication
/// failures, insufficient permissions to issue DDL, network errors, or any
/// other underlying `sqlx::Error` returned while creating the schema.
///
/// # Example
///
/// ```rust,no_run
/// use torrust_tracker_configuration::Core;
/// use torrust_tracker_core::databases::setup::initialize_database;
///
/// // Create a default configuration (ensure it is properly set up for your environment)
/// let config = Core::default();
///
/// // Initialize the database; this will panic if initialization fails.
/// # async {
/// let stores = initialize_database(&config).await;
/// # };
/// ```
#[must_use]
pub async fn initialize_database(config: &Core) -> DatabaseStores {
let driver = match config.database.driver {
torrust_tracker_configuration::Driver::Sqlite3 => Driver::Sqlite3,
torrust_tracker_configuration::Driver::MySQL => Driver::MySQL,
torrust_tracker_configuration::Driver::PostgreSQL => Driver::PostgreSQL,
};
match driver {
Driver::Sqlite3 => {
let db = Arc::new(Sqlite::new(&config.database.path).expect("Database driver build failed."));
db.create_database_tables().await.expect("Could not create database tables.");
build_database_stores(db)
}
Driver::MySQL => {
let db = Arc::new(Mysql::new(&config.database.path).expect("Database driver build failed."));
db.create_database_tables().await.expect("Could not create database tables.");
build_database_stores(db)
}
Driver::PostgreSQL => {
let db = Arc::new(Postgres::new(&config.database.path).expect("Database driver build failed."));
db.create_database_tables().await.expect("Could not create database tables.");
build_database_stores(db)
}
}
}
#[cfg(test)]
mod tests {
use super::initialize_database;
use crate::test_helpers::tests::ephemeral_configuration;
#[tokio::test]
async fn it_should_initialize_the_sqlite_database() {
let config = ephemeral_configuration();
let _database = initialize_database(&config).await;
}
}