diff --git a/crates/deadpool-memcached/README.md b/crates/deadpool-memcached/README.md index e57e1479..35f846fb 100644 --- a/crates/deadpool-memcached/README.md +++ b/crates/deadpool-memcached/README.md @@ -13,11 +13,15 @@ use deadpool_memcached::Manager; #[tokio::main] async fn main() { let manager = Manager::new("localhost:11211"); + let pool = deadpool_memcached::Pool::builder(manager).build().unwrap(); let mut client = pool.get().await.unwrap(); println!("version: {:?}", client.version().await); } ``` +You can also use `deadpool_memcached::Config` to build a pool (including +optional `serde`-based config deserialization). + ## License Licensed under the MIT license ([LICENSE-MIT](LICENSE-MIT) or ). diff --git a/crates/deadpool-memcached/src/config.rs b/crates/deadpool-memcached/src/config.rs new file mode 100644 index 00000000..b6f3318d --- /dev/null +++ b/crates/deadpool-memcached/src/config.rs @@ -0,0 +1,106 @@ +use std::convert::Infallible; + +use crate::{CreatePoolError, Manager, Pool, PoolBuilder, PoolConfig}; + +/// Configuration object. +/// +/// # Example (from environment) +/// +/// You can read the configuration using the +/// [`config`](https://crates.io/crates/config) crate as following: +/// ```env +/// MEMCACHED__ADDR=127.0.0.1:11211 +/// MEMCACHED__POOL__MAX_SIZE=16 +/// MEMCACHED__POOL__TIMEOUTS__WAIT__SECS=5 +/// MEMCACHED__POOL__TIMEOUTS__WAIT__NANOS=0 +/// ``` +/// ```rust,ignore +/// #[derive(serde::Deserialize, serde::Serialize)] +/// struct Config { +/// memcached: deadpool_memcached::Config, +/// } +/// impl Config { +/// pub fn from_env() -> Result { +/// let mut cfg = config::Config::builder() +/// .add_source(config::Environment::default().separator("__")) +/// .build()?; +/// cfg.try_deserialize() +/// } +/// } +/// ``` +#[derive(Clone, Debug, Default)] +pub struct Config { + /// Memcached address. + pub addr: Option, + + /// [`Pool`] configuration. + pub pool: Option, +} + +impl Config { + /// Creates a new [`Config`] with the given Memcached address. + #[must_use] + pub fn new(addr: impl Into) -> Self { + Self { + addr: Some(addr.into()), + pool: None, + } + } + + /// Creates a new [`Pool`] using this [`Config`]. + /// + /// # Errors + /// + /// See [`CreatePoolError`] for details. + pub fn create_pool(&self) -> Result { + self.builder() + .map_err(CreatePoolError::Config)? + .build() + .map_err(CreatePoolError::Build) + } + + /// Creates a new [`PoolBuilder`] using this [`Config`]. + /// + /// # Errors + /// + /// See [`ConfigError`] for details. + pub fn builder(&self) -> Result { + let manager = Manager::new(self.get_addr()); + Ok(Pool::builder(manager).config(self.get_pool_config())) + } + + /// Returns address used to connect to the Memcached server. + pub fn get_addr(&self) -> &str { + self.addr.as_deref().unwrap_or("127.0.0.1:11211") + } + + /// Returns [`deadpool::managed::PoolConfig`] which can be used to construct + /// a [`deadpool::managed::Pool`] instance. + #[must_use] + pub fn get_pool_config(&self) -> PoolConfig { + self.pool.unwrap_or_default() + } +} + +/// This error is returned if there is something wrong with the Memcached configuration. +/// +/// This is just a type alias to [`Infallible`] at the moment as there +/// is no validation happening at the configuration phase. +pub type ConfigError = Infallible; + +#[cfg(test)] +mod tests { + use super::Config; + + #[test] + fn default_addr_is_localhost() { + let cfg = Config::default(); + assert_eq!(cfg.get_addr(), "127.0.0.1:11211"); + } + + #[test] + fn new_sets_addr() { + let cfg = Config::new("cache.internal:11211"); + assert_eq!(cfg.get_addr(), "cache.internal:11211"); + } +} diff --git a/crates/deadpool-memcached/src/lib.rs b/crates/deadpool-memcached/src/lib.rs index b150de4c..66bd128f 100644 --- a/crates/deadpool-memcached/src/lib.rs +++ b/crates/deadpool-memcached/src/lib.rs @@ -7,15 +7,13 @@ //! connect via TCP as there is no existing mechanism to parameterize how to deal with different //! unerlying connection types at the moment. #![deny(warnings, missing_docs)] -use std::convert::Infallible; - use async_memcached::{Client, Error}; +mod config; /// Type alias for using [`deadpool::managed::RecycleResult`] with [`redis`]. type RecycleResult = deadpool::managed::RecycleResult; -type ConfigError = Infallible; - +pub use self::config::{Config, ConfigError}; pub use deadpool::managed::reexports::*; deadpool::managed_reexports!( "memcached", diff --git a/crates/deadpool-memcached/tests/basic.rs b/crates/deadpool-memcached/tests/basic.rs index 4cd650f3..029b9cc4 100644 --- a/crates/deadpool-memcached/tests/basic.rs +++ b/crates/deadpool-memcached/tests/basic.rs @@ -4,17 +4,20 @@ use std::env; use async_memcached::AsciiProtocol; use deadpool_memcached::{Manager, Pool}; -fn create_pool() -> Pool { - let addr = env::var("MEMCACHED__ADDR").unwrap(); +fn create_pool() -> Option { + let addr = env::var("MEMCACHED__ADDR").ok()?; let manager = Manager::new(addr); - Pool::builder(manager).build().unwrap() + Some(Pool::builder(manager).build().unwrap()) } #[tokio::test] async fn test_set_get() { + let Some(pool) = create_pool() else { + // Skip test when no Memcached server is configured. + return; + }; let test_key = "test:basic:test_set_get"; let test_value = "answer_42"; - let pool = create_pool(); let mut conn = pool.get().await.unwrap(); let _ = conn.delete(test_key).await; assert_eq!(conn.get(test_key).await.unwrap(), None);