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
//! This module handles a database layer, mainly intended to be used
//! with a web server or framework like Rocket or Iron.
//!
//! It leverages diesel and r2d2.
//!
//! Most code comes from the Rocket manual:
//! https://rocket.rs/guide/state/#databases
use diesel::Connection;
use diesel::pg::PgConnection;
use diesel::r2d2::ConnectionManager;
use diesel::r2d2::Pool;
use diesel::r2d2::PooledConnection;
use ErrorResult;
use r2d2::Error as PoolError;
use rocket::http::Status;
use rocket::Outcome;
use rocket::request::FromRequest;
use rocket::request::Outcome as RequestOutcome;
use rocket::Request;
use rocket::State;
use std::ops::Deref;
/* -------------------------------------------------------------
Custom types
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pub type PostgreSQLPool = Pool<ConnectionManager<PgConnection>>;
/* -------------------------------------------------------------
DatabaseConnection
:: FromRequest
:: Deref
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/// Represents an established working database connection from the pool
pub struct DatabaseConnection(pub PooledConnection<ConnectionManager<PgConnection>>);
impl<'a, 'r> FromRequest<'a, 'r> for DatabaseConnection {
type Error = ();
fn from_request(request: &'a Request<'r>) -> RequestOutcome<Self, Self::Error> {
let pool = request.guard::<State<PostgreSQLPool>>()?;
match pool.get() {
Ok(connection) => Outcome::Success(DatabaseConnection(connection)),
Err(error) => {
warn!(target:"request", "Can't get a connection from the pool: {}", error);
Outcome::Failure((Status::ServiceUnavailable, ()))
},
}
}
}
impl Deref for DatabaseConnection {
type Target = PgConnection;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/* -------------------------------------------------------------
Helper methods to get a database connection
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/// Builds a r2d2 database pool, to be used in a request guard or a managed state.
///
/// # Examples
///
/// ```
/// rocket::ignite()
/// .manage(initialize_database_pool(String::from("postgres://::1/test"), 4)?)
/// .mount("/", routes)
/// .launch();
/// ```
pub fn initialize_database_pool(url: &str, max_size: u32) -> Result<PostgreSQLPool, PoolError> {
let manager = ConnectionManager::<PgConnection>::new(url);
Pool::builder()
.max_size(max_size)
.build(manager)
}
/// Allows to test if it's possible to establish a connection to the database.
///
/// The goal is to test early any issue with the connection, and loudly warn or fail
/// if the database can't be reached.
///
/// # Examples
///
/// ```
/// // Initial connection to test if the database configuration works
/// {
/// test_database_connection(&config.database_url)?;
/// info!(target: "runner", "Connection to database established.");
/// }
/// ```
pub fn test_database_connection(database_url: &str) -> ErrorResult<()> {
PgConnection::establish(database_url)?;
Ok(())
}