mirror of https://github.com/rwf2/Rocket.git
Add 'PoolResult' alias; rename 'DbError' to 'Error'.
This commit is contained in:
parent
f5fd1007e2
commit
aabbfcfe94
|
@ -394,7 +394,7 @@ use self::r2d2::ManageConnection;
|
|||
#[cfg(feature = "memcache_pool")] pub extern crate memcache;
|
||||
#[cfg(feature = "memcache_pool")] pub extern crate r2d2_memcache;
|
||||
|
||||
/// A structure representing a particular database configuration.
|
||||
/// A default, helper `Config` for any `Poolable` type.
|
||||
///
|
||||
/// For the following configuration:
|
||||
///
|
||||
|
@ -405,19 +405,20 @@ use self::r2d2::ManageConnection;
|
|||
/// timeout = 5
|
||||
/// ```
|
||||
///
|
||||
/// would generate the following struct:
|
||||
/// ...`Config::from("my_database", cargo)` would return the following struct:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # use rocket_contrib::databases::Config;
|
||||
/// Config {
|
||||
/// url: "postgres://root:root@localhost/my_database",
|
||||
/// url: "postgres://root:root@localhost/my_database".into(),
|
||||
/// pool_size: 10,
|
||||
/// timeout: 5
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// If you want to implement your own custom database adapter (or other
|
||||
/// database-like struct that can be pooled by `r2d2`) and need some more
|
||||
/// configurations options, you need to implement your own config struct.
|
||||
/// configurations options, you may need to define a custom `Config` struct.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
/// Connection URL specified in the Rocket configuration.
|
||||
|
@ -431,61 +432,51 @@ pub struct Config {
|
|||
}
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use rocket::figment::{Figment, Error, providers::Serialized};
|
||||
use rocket::figment::{self, Figment, providers::Serialized};
|
||||
|
||||
impl Config {
|
||||
/// Retrieves the database configuration for the database named `name`.
|
||||
///
|
||||
/// This function is primarily used by the code generated by the `#[database]`
|
||||
/// attribute.
|
||||
/// This function is primarily used by the generated code from the
|
||||
/// `#[database]` attribute.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Consider the following configuration:
|
||||
///
|
||||
/// ```toml
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "diesel_sqlite_pool")] {
|
||||
/// # use rocket::figment::{Figment, providers::{Format, Toml}};
|
||||
/// // Assume that these are the contents of `Rocket.toml`:
|
||||
/// # let toml = Toml::string(r#"
|
||||
/// [global.databases]
|
||||
/// my_db = { url = "db/db.sqlite", pool_size = 25 }
|
||||
/// my_other_db = { url = "mysql://root:root@localhost/database" }
|
||||
/// ```
|
||||
/// # "#).nested();
|
||||
///
|
||||
/// Which can be extracted like following:
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "diesel_sqlite_pool")] {
|
||||
/// # use rocket::figment::{value::{Map, Value}, util::map};
|
||||
/// use rocket_contrib::databases::Config;
|
||||
///
|
||||
/// # #[rocket::launch]
|
||||
/// # async fn rocket() -> _ {
|
||||
/// # let db: Map<_, Value> = map! {
|
||||
/// # "url" => "db/db.sqlite".into(),
|
||||
/// # "pool_size" => 25.into()
|
||||
/// # };
|
||||
/// # let other_db: Map<_, Value> = map! {
|
||||
/// # "url" => "mysql://root:root@localhost/database".into()
|
||||
/// # };
|
||||
/// fn pool(cargo: &rocket::Cargo) {
|
||||
/// let config = Config::from("my_db", cargo).unwrap();
|
||||
/// assert_eq!(config.url, "db/db.sqlite");
|
||||
/// assert_eq!(config.pool_size, 25);
|
||||
///
|
||||
/// let config = Config::from("my_other_db", cargo).unwrap();
|
||||
/// assert_eq!(config.url, "mysql://root:root@localhost/database");
|
||||
/// assert_eq!(config.pool_size, cargo.config().workers as u32);
|
||||
///
|
||||
/// let config = Config::from("unknown_db", cargo);
|
||||
/// assert!(config.is_err())
|
||||
/// }
|
||||
/// #
|
||||
/// # let figment = rocket::Config::figment()
|
||||
/// # .merge(("databases", map!["my_db" => db]))
|
||||
/// # .merge(("databases", map!["my_other_db" => other_db]));
|
||||
/// #
|
||||
/// # let mut rocket = rocket::custom(figment);
|
||||
/// // Access the global config
|
||||
/// let cargo = rocket.inspect().await;
|
||||
/// // Create a db config from the global one
|
||||
/// let db_config = Config::from(cargo, "my_db").expect("my_db config");
|
||||
/// // The same with different error handling
|
||||
/// let other_db_config = match Config::from(cargo, "my_other_db") {
|
||||
/// Ok(config) => config,
|
||||
/// Err(err) => panic!("Couldn't read my_other_db config because of error: {:?}", err),
|
||||
/// };
|
||||
/// # rocket
|
||||
/// # }
|
||||
/// # rocket::async_test(async {
|
||||
/// # let config = Figment::from(rocket::Config::default()).merge(toml);
|
||||
/// # let mut rocket = rocket::custom(config);
|
||||
/// # let cargo = rocket.inspect().await;
|
||||
/// # pool(cargo);
|
||||
/// # });
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from(cargo: &rocket::Cargo, db: &str) -> Result<Config, Error> {
|
||||
let db_key = format!("databases.{}", db);
|
||||
pub fn from(db_name: &str, cargo: &rocket::Cargo) -> Result<Config, figment::Error> {
|
||||
let db_key = format!("databases.{}", db_name);
|
||||
let key = |name: &str| format!("{}.{}", db_key, name);
|
||||
Figment::from(cargo.figment())
|
||||
.merge(Serialized::default(&key("pool_size"), cargo.config().workers))
|
||||
|
@ -499,18 +490,24 @@ impl Config {
|
|||
/// This type is only relevant to implementors of the [`Poolable`] trait. See
|
||||
/// the [`Poolable`] documentation for more information on how to use this type.
|
||||
#[derive(Debug)]
|
||||
pub enum DbError<T> {
|
||||
/// The custom error type to wrap alongside `r2d2::Error`.
|
||||
pub enum Error<T> {
|
||||
/// A custom error of type `T`.
|
||||
Custom(T),
|
||||
/// The error returned by an r2d2 pool.
|
||||
PoolError(r2d2::Error),
|
||||
/// The error returned by `Figment` when a config fails to deserialize.
|
||||
Config(Error),
|
||||
/// An error occurred while initializing an `r2d2` pool.
|
||||
Pool(r2d2::Error),
|
||||
/// An error occurred while extracting a `figment` configuration.
|
||||
Config(figment::Error),
|
||||
}
|
||||
|
||||
impl<T> From<Error> for DbError<T> {
|
||||
fn from(error: Error) -> Self {
|
||||
DbError::Config(error)
|
||||
impl<T> From<figment::Error> for Error<T> {
|
||||
fn from(error: figment::Error) -> Self {
|
||||
Error::Config(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<r2d2::Error> for Error<T> {
|
||||
fn from(error: r2d2::Error) -> Self {
|
||||
Error::Pool(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,7 +526,7 @@ impl<T> From<Error> for DbError<T> {
|
|||
///
|
||||
/// # Implementation Guide
|
||||
///
|
||||
/// As a r2d2-compatible database (or other resource) adapter provider,
|
||||
/// As an r2d2-compatible database (or other resource) adapter provider,
|
||||
/// implementing `Poolable` in your own library will enable Rocket users to
|
||||
/// consume your adapter with its built-in connection pooling support.
|
||||
///
|
||||
|
@ -548,7 +545,6 @@ impl<T> From<Error> for DbError<T> {
|
|||
/// `Poolable` for `foo::Connection`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket_contrib::databases::{r2d2, DbError, Config, Poolable};
|
||||
/// # mod foo {
|
||||
/// # use std::fmt;
|
||||
/// # use rocket_contrib::databases::r2d2;
|
||||
|
@ -575,20 +571,16 @@ impl<T> From<Error> for DbError<T> {
|
|||
/// # fn has_broken(&self, _: &mut Connection) -> bool { panic!() }
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// use rocket_contrib::databases::{r2d2, Error, Config, Poolable, PoolResult};
|
||||
///
|
||||
/// impl Poolable for foo::Connection {
|
||||
/// type Manager = foo::ConnectionManager;
|
||||
/// type Error = DbError<foo::Error>;
|
||||
/// type Error = foo::Error;
|
||||
///
|
||||
/// fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
/// let config = Config::from(cargo, name)?;
|
||||
/// let manager = foo::ConnectionManager::new(&config.url)
|
||||
/// .map_err(DbError::Custom)?;
|
||||
///
|
||||
/// r2d2::Pool::builder()
|
||||
/// .max_size(config.pool_size)
|
||||
/// .build(manager)
|
||||
/// .map_err(DbError::PoolError)
|
||||
/// fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
/// let config = Config::from(db_name, cargo)?;
|
||||
/// let manager = foo::ConnectionManager::new(&config.url).map_err(Error::Custom)?;
|
||||
/// Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -598,113 +590,114 @@ impl<T> From<Error> for DbError<T> {
|
|||
/// error type and the `r2d2::Error` type that can result from
|
||||
/// `r2d2::Pool::builder()` or `database::Config::from()`.
|
||||
///
|
||||
/// For more concrete examples, consult Rocket's existing implementations of [`Poolable`].
|
||||
/// In the event that a connection manager isn't fallible (as is the case with
|
||||
/// Diesel's r2d2 connection manager, for instance), the associated error type
|
||||
/// for the `Poolable` implementation should be `std::convert::Infallible`.
|
||||
///
|
||||
/// For more concrete example, consult Rocket's existing implementations of
|
||||
/// [`Poolable`].
|
||||
pub trait Poolable: Send + Sized + 'static {
|
||||
/// The associated connection manager for the given connection type.
|
||||
type Manager: ManageConnection<Connection=Self>;
|
||||
|
||||
/// The associated error type in the event that constructing the connection
|
||||
/// manager and/or the connection pool fails.
|
||||
type Error: std::fmt::Debug;
|
||||
|
||||
/// Creates an `r2d2` connection pool for `Manager::Connection`, returning
|
||||
/// the pool on success.
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error>;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self>;
|
||||
}
|
||||
|
||||
/// A type alias for the return type of [`Poolable::pool()`].
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type PoolResult<P: Poolable> = Result<r2d2::Pool<P::Manager>, Error<P::Error>>;
|
||||
|
||||
#[cfg(feature = "diesel_sqlite_pool")]
|
||||
impl Poolable for diesel::SqliteConnection {
|
||||
type Manager = diesel::r2d2::ConnectionManager<diesel::SqliteConnection>;
|
||||
type Error = DbError<r2d2::Error>;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let manager = diesel::r2d2::ConnectionManager::new(&config.url);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diesel_postgres_pool")]
|
||||
impl Poolable for diesel::PgConnection {
|
||||
type Manager = diesel::r2d2::ConnectionManager<diesel::PgConnection>;
|
||||
type Error = DbError<r2d2::Error>;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let manager = diesel::r2d2::ConnectionManager::new(&config.url);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diesel_mysql_pool")]
|
||||
impl Poolable for diesel::MysqlConnection {
|
||||
type Manager = diesel::r2d2::ConnectionManager<diesel::MysqlConnection>;
|
||||
type Error = DbError<r2d2::Error>;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let manager = diesel::r2d2::ConnectionManager::new(&config.url);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Come up with a way to handle TLS
|
||||
// TODO: Add a feature to enable TLS in `postgres`; parse a suitable `config`.
|
||||
#[cfg(feature = "postgres_pool")]
|
||||
impl Poolable for postgres::Client {
|
||||
type Manager = r2d2_postgres::PostgresConnectionManager<postgres::tls::NoTls>;
|
||||
type Error = DbError<postgres::Error>;
|
||||
type Error = postgres::Error;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
let manager = r2d2_postgres::PostgresConnectionManager::new(
|
||||
config.url.parse().map_err(DbError::Custom)?,
|
||||
postgres::tls::NoTls,
|
||||
);
|
||||
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let url = config.url.parse().map_err(Error::Custom)?;
|
||||
let manager = r2d2_postgres::PostgresConnectionManager::new(url, postgres::tls::NoTls);
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mysql_pool")]
|
||||
impl Poolable for mysql::Conn {
|
||||
type Manager = r2d2_mysql::MysqlConnectionManager;
|
||||
type Error = DbError<r2d2::Error>;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let opts = mysql::OptsBuilder::from_opts(&config.url);
|
||||
let manager = r2d2_mysql::MysqlConnectionManager::new(opts);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sqlite_pool")]
|
||||
impl Poolable for rusqlite::Connection {
|
||||
type Manager = r2d2_sqlite::SqliteConnectionManager;
|
||||
type Error = DbError<r2d2::Error>;
|
||||
type Error = std::convert::Infallible;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let manager = r2d2_sqlite::SqliteConnectionManager::file(&*config.url);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "memcache_pool")]
|
||||
impl Poolable for memcache::Client {
|
||||
type Manager = r2d2_memcache::MemcacheConnectionManager;
|
||||
type Error = DbError<memcache::MemcacheError>;
|
||||
// Unused, but we might want it in the future without a breaking change.
|
||||
type Error = memcache::MemcacheError;
|
||||
|
||||
fn pool(cargo: &rocket::Cargo, name: &str) -> Result<r2d2::Pool<Self::Manager>, Self::Error> {
|
||||
let config = Config::from(cargo, name)?;
|
||||
fn pool(db_name: &str, cargo: &rocket::Cargo) -> PoolResult<Self> {
|
||||
let config = Config::from(db_name, cargo)?;
|
||||
let manager = r2d2_memcache::MemcacheConnectionManager::new(&*config.url);
|
||||
r2d2::Pool::builder().max_size(config.pool_size).build(manager)
|
||||
.map_err(DbError::PoolError)
|
||||
Ok(r2d2::Pool::builder().max_size(config.pool_size).build(manager)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,7 +752,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
|
|||
pub fn fairing(fairing_name: &'static str, db_name: &'static str) -> impl Fairing {
|
||||
AdHoc::on_attach(fairing_name, move |mut rocket| async move {
|
||||
let cargo = rocket.inspect().await;
|
||||
let config = match Config::from(cargo, db_name) {
|
||||
let config = match Config::from(db_name, cargo) {
|
||||
Ok(config) => config,
|
||||
Err(config_error) => {
|
||||
rocket::error!("database configuration error for '{}'", db_name);
|
||||
|
@ -768,7 +761,7 @@ impl<K: 'static, C: Poolable> ConnectionPool<K, C> {
|
|||
}
|
||||
};
|
||||
|
||||
match C::pool(cargo, db_name) {
|
||||
match C::pool(db_name, cargo) {
|
||||
Ok(pool) => {
|
||||
let pool_size = config.pool_size;
|
||||
let managed = ConnectionPool::<K, C> {
|
||||
|
|
Loading…
Reference in New Issue