1use deadpool_postgres::{BuildError, Manager, ManagerConfig, Pool, PoolError, RecyclingMethod, Runtime};
2use thiserror::Error;
3use tokio_postgres::{Config, Error as PgError, NoTls, Row, types::ToSql};
4
5#[derive(Debug, Error)]
7pub enum DbError {
8 #[error("Failed to acquire connection from pool: {0}")]
9 Pool(#[from] PoolError),
10
11 #[error("Database query error: {0}")]
12 Query(#[from] PgError),
13
14 #[error("Invalid database configuration: {0}")]
15 Config(String),
16
17 #[error("Failed to build connection pool: {0}")]
18 Build(#[from] BuildError),
19}
20
21#[derive(Clone)]
23pub struct DbPool {
24 pool: Pool,
25}
26
27impl DbPool {
28 pub fn new(database_url: &str, max_connections: usize) -> Result<Self, DbError> {
31 let config: Config = database_url
32 .parse()
33 .map_err(|e| DbError::Config(format!("{}", e)))?;
34
35 let mgr_config = ManagerConfig {
36 recycling_method: RecyclingMethod::Fast,
37 };
38
39 let manager = Manager::from_config(config, NoTls, mgr_config);
40
41 let pool = Pool::builder(manager)
43 .max_size(max_connections)
44 .runtime(Runtime::Tokio1)
45 .build()?;
46
47 Ok(DbPool { pool })
48 }
49
50 #[inline]
53 pub async fn execute(
54 &self,
55 query: &str,
56 params: &[&(dyn ToSql + Sync)],
57 ) -> Result<u64, DbError> {
58 let client = self.pool.get().await?;
59 let affected = client.execute(query, params).await?;
60 Ok(affected)
61 }
62
63 #[inline]
65 pub async fn query_one(
66 &self,
67 query: &str,
68 params: &[&(dyn ToSql + Sync)],
69 ) -> Result<Row, DbError> {
70 let client = self.pool.get().await?;
71 let row = client.query_one(query, params).await?;
72 Ok(row)
73 }
74
75 #[inline]
77 pub async fn query_opt(
78 &self,
79 query: &str,
80 params: &[&(dyn ToSql + Sync)],
81 ) -> Result<Option<Row>, DbError> {
82 let client = self.pool.get().await?;
83 let row_opt = client.query_opt(query, params).await?;
84 Ok(row_opt)
85 }
86
87 #[inline]
89 pub async fn query(
90 &self,
91 query: &str,
92 params: &[&(dyn ToSql + Sync)],
93 ) -> Result<Vec<Row>, DbError> {
94 let client = self.pool.get().await?;
95 let rows = client.query(query, params).await?;
96 Ok(rows)
97 }
98}