Skip to main content

sqlx_pool_registry/
registry.rs

1use crate::{db_pools::DbPools, provider::PoolProvider};
2use std::{collections::HashMap, error::Error, fmt};
3
4/// Error returned when a named pool is not registered.
5///
6/// Available with the `with-named-pools` feature.
7#[derive(Clone, Debug, Eq, PartialEq)]
8pub struct UnknownPool {
9    name: String,
10}
11
12impl UnknownPool {
13    fn new(name: &str) -> Self {
14        Self {
15            name: name.to_owned(),
16        }
17    }
18
19    /// Return the name that was not found.
20    pub fn name(&self) -> &str {
21        &self.name
22    }
23}
24
25impl fmt::Display for UnknownPool {
26    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27        write!(formatter, "unknown pool `{}`", self.name)
28    }
29}
30
31impl Error for UnknownPool {}
32
33/// A registry of database pool providers keyed by name.
34///
35/// `PoolRegistry` is available with the `with-named-pools` feature. It stores
36/// one homogeneous provider type and defaults to [`DbPools`]. Selecting a name
37/// returns the provider for that name; the registry itself does not keep a
38/// stateful "current" pool and does not implement [`PoolProvider`].
39///
40/// # Examples
41///
42/// ```rust
43/// use sqlx_pool_registry::sqlx::{self, postgres::PgPoolOptions};
44/// use sqlx_pool_registry::{DbPools, PoolProvider, PoolRegistry};
45///
46/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
47/// let auth_primary = PgPoolOptions::new()
48///     .connect_lazy("postgresql://localhost/auth")?;
49/// let auth_replica = PgPoolOptions::new()
50///     .connect_lazy("postgresql://localhost/auth_replica")?;
51/// let analytics = PgPoolOptions::new()
52///     .connect_lazy("postgresql://localhost/analytics")?;
53///
54/// let mut pools = PoolRegistry::new();
55/// pools.insert("auth", DbPools::with_replica(auth_primary, auth_replica));
56/// pools.insert("analytics", DbPools::new(analytics));
57///
58/// let auth = pools.try_get("auth")?;
59/// let _primary = auth.primary();
60/// let _replica = auth.replica(); // Option<&PgPool>, with no fallback
61/// let _read = auth.read(); // Replica, or primary when no replica is configured
62/// # Ok(())
63/// # }
64/// ```
65#[derive(Clone, Debug)]
66pub struct PoolRegistry<P: PoolProvider = DbPools> {
67    pools: HashMap<String, P>,
68}
69
70impl<P: PoolProvider> PoolRegistry<P> {
71    /// Create an empty pool registry.
72    pub fn new() -> Self {
73        Self {
74            pools: HashMap::new(),
75        }
76    }
77
78    /// Insert a provider under `name`.
79    ///
80    /// If the name was already registered, the previous provider is replaced
81    /// and returned.
82    pub fn insert(&mut self, name: impl Into<String>, provider: P) -> Option<P> {
83        self.pools.insert(name.into(), provider)
84    }
85
86    /// Get a provider by name.
87    ///
88    /// Names are matched exactly and case-sensitively.
89    pub fn get(&self, name: &str) -> Option<&P> {
90        self.pools.get(name)
91    }
92
93    /// Get a provider by name, returning [`UnknownPool`] when it is absent.
94    pub fn try_get(&self, name: &str) -> Result<&P, UnknownPool> {
95        self.get(name).ok_or_else(|| UnknownPool::new(name))
96    }
97
98    /// Return whether a provider is registered under `name`.
99    pub fn contains_key(&self, name: &str) -> bool {
100        self.pools.contains_key(name)
101    }
102
103    /// Return the number of registered providers.
104    pub fn len(&self) -> usize {
105        self.pools.len()
106    }
107
108    /// Return whether the registry contains no providers.
109    pub fn is_empty(&self) -> bool {
110        self.pools.is_empty()
111    }
112
113    /// Iterate over registered names and providers in unspecified order.
114    pub fn iter(&self) -> impl Iterator<Item = (&str, &P)> {
115        self.pools
116            .iter()
117            .map(|(name, provider)| (name.as_str(), provider))
118    }
119}
120
121impl<P: PoolProvider> Default for PoolRegistry<P> {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl<K, P> FromIterator<(K, P)> for PoolRegistry<P>
128where
129    K: Into<String>,
130    P: PoolProvider,
131{
132    fn from_iter<T: IntoIterator<Item = (K, P)>>(iter: T) -> Self {
133        let mut registry = Self::new();
134        registry.extend(iter);
135        registry
136    }
137}
138
139impl<K, P> Extend<(K, P)> for PoolRegistry<P>
140where
141    K: Into<String>,
142    P: PoolProvider,
143{
144    fn extend<T: IntoIterator<Item = (K, P)>>(&mut self, iter: T) {
145        self.pools.extend(
146            iter.into_iter()
147                .map(|(name, provider)| (name.into(), provider)),
148        );
149    }
150}