From e7a40be58f002d4d350462c0d77e7608812b9880 Mon Sep 17 00:00:00 2001 From: m0n0096 Date: Fri, 10 Jul 2026 14:47:33 -0700 Subject: [PATCH 1/2] add config structs to deadpool-memcached --- crates/deadpool-memcached/Cargo.toml | 4 + crates/deadpool-memcached/README.md | 4 + crates/deadpool-memcached/src/config.rs | 107 ++++++++++++++++++++++++ crates/deadpool-memcached/src/lib.rs | 6 +- 4 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 crates/deadpool-memcached/src/config.rs diff --git a/crates/deadpool-memcached/Cargo.toml b/crates/deadpool-memcached/Cargo.toml index 27a7dfd2..8827588c 100644 --- a/crates/deadpool-memcached/Cargo.toml +++ b/crates/deadpool-memcached/Cargo.toml @@ -19,6 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["tcp"] +serde = ["deadpool/serde", "dep:serde"] # Re-export of async-memcached features polonius = ["async-memcached/polonius"] @@ -35,6 +36,9 @@ deadpool = { path = "../deadpool", version = "0.13.0", default-features = false, # dependency. Once async-memcached is fixed this dependency can be removed # again. tokio = { version = "1.29", default-features = false, features = ["net"] } +serde = { package = "serde", version = "1.0", features = [ + "derive", +], optional = true } [dev-dependencies] tokio = { version = "1.29", features = ["macros", "rt-multi-thread"] } 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..463f5a36 --- /dev/null +++ b/crates/deadpool-memcached/src/config.rs @@ -0,0 +1,107 @@ +use std::convert::Infallible; + +use crate::{CreatePoolError, Manager, Pool, PoolBuilder, PoolConfig}; + +/// Configuration object. +/// +/// # Example (from environment) +/// +/// By enabling the `serde` feature 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 +/// #[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)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +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", From a9162e52b94eca7277f529ab9fd3b0f3e8b89dc3 Mon Sep 17 00:00:00 2001 From: m0n0096 Date: Fri, 10 Jul 2026 14:54:07 -0700 Subject: [PATCH 2/2] add config structs to deadpool-memcached --- crates/deadpool-memcached/src/config.rs | 2 +- crates/deadpool-memcached/tests/basic.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/deadpool-memcached/src/config.rs b/crates/deadpool-memcached/src/config.rs index 463f5a36..55354965 100644 --- a/crates/deadpool-memcached/src/config.rs +++ b/crates/deadpool-memcached/src/config.rs @@ -14,7 +14,7 @@ use crate::{CreatePoolError, Manager, Pool, PoolBuilder, PoolConfig}; /// MEMCACHED__POOL__TIMEOUTS__WAIT__SECS=5 /// MEMCACHED__POOL__TIMEOUTS__WAIT__NANOS=0 /// ``` -/// ```rust +/// ```rust,ignore /// #[derive(serde::Deserialize, serde::Serialize)] /// struct Config { /// memcached: deadpool_memcached::Config, 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);