1use crate::config::{DatabaseBackend, SqlxConfig};
4use crate::error::{SqlxError, SqlxResult};
5
6#[derive(Clone)]
8pub enum SqlxPool {
9 #[cfg(feature = "postgres")]
11 Postgres(sqlx::PgPool),
12 #[cfg(feature = "mysql")]
14 MySql(sqlx::MySqlPool),
15 #[cfg(feature = "sqlite")]
17 Sqlite(sqlx::SqlitePool),
18}
19
20impl SqlxPool {
21 pub async fn connect(config: &SqlxConfig) -> SqlxResult<Self> {
23 match config.backend {
24 #[cfg(feature = "postgres")]
25 DatabaseBackend::Postgres => {
26 let pool = sqlx::postgres::PgPoolOptions::new()
27 .max_connections(config.max_connections)
28 .min_connections(config.min_connections)
29 .acquire_timeout(config.connect_timeout)
30 .idle_timeout(config.idle_timeout)
31 .max_lifetime(config.max_lifetime)
32 .connect(&config.url)
33 .await?;
34 Ok(Self::Postgres(pool))
35 }
36 #[cfg(feature = "mysql")]
37 DatabaseBackend::MySql => {
38 let pool = sqlx::mysql::MySqlPoolOptions::new()
39 .max_connections(config.max_connections)
40 .min_connections(config.min_connections)
41 .acquire_timeout(config.connect_timeout)
42 .idle_timeout(config.idle_timeout)
43 .max_lifetime(config.max_lifetime)
44 .connect(&config.url)
45 .await?;
46 Ok(Self::MySql(pool))
47 }
48 #[cfg(feature = "sqlite")]
49 DatabaseBackend::Sqlite => {
50 let pool = sqlx::sqlite::SqlitePoolOptions::new()
51 .max_connections(config.max_connections)
52 .min_connections(config.min_connections)
53 .acquire_timeout(config.connect_timeout)
54 .idle_timeout(config.idle_timeout)
55 .max_lifetime(config.max_lifetime)
56 .connect(&config.url)
57 .await?;
58 Ok(Self::Sqlite(pool))
59 }
60 #[allow(unreachable_patterns)]
61 _ => Err(SqlxError::config(format!(
62 "Database backend {:?} not enabled. Enable the corresponding feature.",
63 config.backend
64 ))),
65 }
66 }
67
68 pub fn backend(&self) -> DatabaseBackend {
70 match self {
71 #[cfg(feature = "postgres")]
72 Self::Postgres(_) => DatabaseBackend::Postgres,
73 #[cfg(feature = "mysql")]
74 Self::MySql(_) => DatabaseBackend::MySql,
75 #[cfg(feature = "sqlite")]
76 Self::Sqlite(_) => DatabaseBackend::Sqlite,
77 }
78 }
79
80 pub async fn close(&self) {
82 match self {
83 #[cfg(feature = "postgres")]
84 Self::Postgres(pool) => pool.close().await,
85 #[cfg(feature = "mysql")]
86 Self::MySql(pool) => pool.close().await,
87 #[cfg(feature = "sqlite")]
88 Self::Sqlite(pool) => pool.close().await,
89 }
90 }
91
92 pub fn is_closed(&self) -> bool {
94 match self {
95 #[cfg(feature = "postgres")]
96 Self::Postgres(pool) => pool.is_closed(),
97 #[cfg(feature = "mysql")]
98 Self::MySql(pool) => pool.is_closed(),
99 #[cfg(feature = "sqlite")]
100 Self::Sqlite(pool) => pool.is_closed(),
101 }
102 }
103
104 pub fn size(&self) -> u32 {
106 match self {
107 #[cfg(feature = "postgres")]
108 Self::Postgres(pool) => pool.size(),
109 #[cfg(feature = "mysql")]
110 Self::MySql(pool) => pool.size(),
111 #[cfg(feature = "sqlite")]
112 Self::Sqlite(pool) => pool.size(),
113 }
114 }
115
116 pub fn num_idle(&self) -> usize {
118 match self {
119 #[cfg(feature = "postgres")]
120 Self::Postgres(pool) => pool.num_idle(),
121 #[cfg(feature = "mysql")]
122 Self::MySql(pool) => pool.num_idle(),
123 #[cfg(feature = "sqlite")]
124 Self::Sqlite(pool) => pool.num_idle(),
125 }
126 }
127
128 #[cfg(feature = "postgres")]
130 pub fn as_postgres(&self) -> Option<&sqlx::PgPool> {
131 match self {
132 Self::Postgres(pool) => Some(pool),
133 #[allow(unreachable_patterns)]
134 _ => None,
135 }
136 }
137
138 #[cfg(feature = "mysql")]
140 pub fn as_mysql(&self) -> Option<&sqlx::MySqlPool> {
141 match self {
142 Self::MySql(pool) => Some(pool),
143 #[allow(unreachable_patterns)]
144 _ => None,
145 }
146 }
147
148 #[cfg(feature = "sqlite")]
150 pub fn as_sqlite(&self) -> Option<&sqlx::SqlitePool> {
151 match self {
152 Self::Sqlite(pool) => Some(pool),
153 #[allow(unreachable_patterns)]
154 _ => None,
155 }
156 }
157}
158
159pub struct SqlxPoolBuilder {
161 config: SqlxConfig,
162}
163
164impl SqlxPoolBuilder {
165 pub fn new(config: SqlxConfig) -> Self {
167 Self { config }
168 }
169
170 pub fn from_url(url: impl Into<String>) -> SqlxResult<Self> {
172 let config = SqlxConfig::from_url(url)?;
173 Ok(Self { config })
174 }
175
176 pub fn max_connections(mut self, max: u32) -> Self {
178 self.config.max_connections = max;
179 self
180 }
181
182 pub fn min_connections(mut self, min: u32) -> Self {
184 self.config.min_connections = min;
185 self
186 }
187
188 pub async fn build(self) -> SqlxResult<SqlxPool> {
190 SqlxPool::connect(&self.config).await
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct PoolStatus {
197 pub size: u32,
199 pub idle: usize,
201 pub is_closed: bool,
203 pub backend: DatabaseBackend,
205}
206
207impl SqlxPool {
208 pub fn status(&self) -> PoolStatus {
210 PoolStatus {
211 size: self.size(),
212 idle: self.num_idle(),
213 is_closed: self.is_closed(),
214 backend: self.backend(),
215 }
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_pool_builder() {
225 let builder = SqlxPoolBuilder::from_url("postgres://localhost/test").unwrap();
226 let builder = builder.max_connections(20).min_connections(5);
227 assert_eq!(builder.config.max_connections, 20);
228 assert_eq!(builder.config.min_connections, 5);
229 }
230}