1#[cfg(feature = "postgres")]
4use bytes::BytesMut;
5use serde::{Deserialize, Serialize};
6#[cfg(feature = "postgres")]
7use tokio_postgres::types::{IsNull, ToSql, Type};
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[serde(rename_all = "lowercase")]
35pub enum DatabaseType {
36 PostgreSQL,
38 MySQL,
40 SQLite,
42 SQLServer,
44}
45
46impl DatabaseType {
47 #[must_use]
49 pub const fn as_str(&self) -> &'static str {
50 match self {
51 Self::PostgreSQL => "postgresql",
52 Self::MySQL => "mysql",
53 Self::SQLite => "sqlite",
54 Self::SQLServer => "sqlserver",
55 }
56 }
57}
58
59impl std::fmt::Display for DatabaseType {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.write_str(self.as_str())
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct JsonbValue {
70 pub data: serde_json::Value,
72}
73
74impl JsonbValue {
75 #[must_use]
77 pub const fn new(data: serde_json::Value) -> Self {
78 Self { data }
79 }
80
81 #[must_use]
83 pub const fn as_value(&self) -> &serde_json::Value {
84 &self.data
85 }
86
87 #[must_use]
89 pub fn into_value(self) -> serde_json::Value {
90 self.data
91 }
92}
93
94#[cfg(feature = "postgres")]
99#[derive(Debug, Clone)]
100#[non_exhaustive]
101pub enum QueryParam {
102 Null,
104 Bool(bool),
106 Int(i32),
108 BigInt(i64),
110 Float(f32),
112 Double(f64),
114 Text(String),
116 Json(serde_json::Value),
118}
119
120#[cfg(feature = "postgres")]
121impl From<serde_json::Value> for QueryParam {
122 fn from(value: serde_json::Value) -> Self {
123 match value {
124 serde_json::Value::Null => Self::Null,
125 serde_json::Value::Bool(b) => Self::Bool(b),
126 serde_json::Value::Number(n) => {
127 Self::Text(n.to_string())
131 },
132 serde_json::Value::String(s) => Self::Text(s),
133 serde_json::Value::Array(_) | serde_json::Value::Object(_) => Self::Json(value),
134 }
135 }
136}
137
138#[cfg(feature = "postgres")]
139impl ToSql for QueryParam {
140 tokio_postgres::types::to_sql_checked!();
141
142 fn to_sql(
143 &self,
144 ty: &Type,
145 out: &mut BytesMut,
146 ) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
147 match self {
148 Self::Null => Ok(IsNull::Yes),
149 Self::Bool(b) => b.to_sql(ty, out),
150 Self::Int(i) => i.to_sql(ty, out),
151 Self::BigInt(i) => i.to_sql(ty, out),
152 Self::Float(f) => f.to_sql(ty, out),
153 Self::Double(f) => f.to_sql(ty, out),
154 Self::Text(s) => s.to_sql(ty, out),
155 Self::Json(v) => v.to_sql(ty, out),
156 }
157 }
158
159 fn accepts(_ty: &Type) -> bool {
160 true
161 }
162}
163
164#[cfg(feature = "postgres")]
183pub fn to_sql_param(param: &QueryParam) -> Box<dyn ToSql + Sync + Send> {
184 match param {
185 QueryParam::Null => Box::new(None::<String>),
186 QueryParam::Bool(b) => Box::new(*b),
187 QueryParam::Int(i) => Box::new(*i),
188 QueryParam::BigInt(i) => Box::new(*i),
189 QueryParam::Float(f) => Box::new(*f),
190 QueryParam::Double(f) => Box::new(*f),
191 QueryParam::Text(s) => Box::new(s.clone()),
192 QueryParam::Json(v) => Box::new(v.clone()),
193 }
194}
195
196#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
198pub struct PoolMetrics {
199 pub total_connections: u32,
201 pub idle_connections: u32,
203 pub active_connections: u32,
205 pub waiting_requests: u32,
207}
208
209impl PoolMetrics {
210 #[must_use]
212 pub fn utilization(&self) -> f64 {
213 if self.total_connections == 0 {
214 return 0.0;
215 }
216 f64::from(self.active_connections) / f64::from(self.total_connections)
217 }
218
219 #[must_use]
221 pub const fn is_exhausted(&self) -> bool {
222 self.idle_connections == 0 && self.waiting_requests > 0
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn test_database_type_as_str() {
232 assert_eq!(DatabaseType::PostgreSQL.as_str(), "postgresql");
233 assert_eq!(DatabaseType::MySQL.as_str(), "mysql");
234 assert_eq!(DatabaseType::SQLite.as_str(), "sqlite");
235 assert_eq!(DatabaseType::SQLServer.as_str(), "sqlserver");
236 }
237
238 #[test]
239 fn test_database_type_display() {
240 assert_eq!(DatabaseType::PostgreSQL.to_string(), "postgresql");
241 }
242
243 #[test]
244 fn test_jsonb_value() {
245 let value = serde_json::json!({"id": "123", "name": "test"});
246 let jsonb = JsonbValue::new(value.clone());
247
248 assert_eq!(jsonb.as_value(), &value);
249 assert_eq!(jsonb.into_value(), value);
250 }
251
252 #[test]
253 fn test_pool_metrics_utilization() {
254 let metrics = PoolMetrics {
255 total_connections: 10,
256 idle_connections: 5,
257 active_connections: 5,
258 waiting_requests: 0,
259 };
260
261 assert!((metrics.utilization() - 0.5).abs() < f64::EPSILON);
262 assert!(!metrics.is_exhausted());
263 }
264
265 #[test]
266 fn test_pool_metrics_exhausted() {
267 let metrics = PoolMetrics {
268 total_connections: 10,
269 idle_connections: 0,
270 active_connections: 10,
271 waiting_requests: 5,
272 };
273
274 assert!((metrics.utilization() - 1.0).abs() < f64::EPSILON);
275 assert!(metrics.is_exhausted());
276 }
277}