fraiseql_core/federation/
connection_manager.rs1use std::{
7 collections::HashMap,
8 sync::{Arc, Mutex},
9};
10
11use crate::{
12 db::traits::DatabaseAdapter,
13 error::{FraiseQLError, Result},
14};
15
16#[derive(Debug, Clone)]
18pub struct RemoteDatabaseConfig {
19 pub connection_string: String,
21 pub pool_size: Option<u32>,
23 pub timeout_seconds: Option<u32>,
25}
26
27impl RemoteDatabaseConfig {
28 pub fn new(connection_string: impl Into<String>) -> Self {
30 Self {
31 connection_string: connection_string.into(),
32 pool_size: None,
33 timeout_seconds: None,
34 }
35 }
36
37 pub fn with_pool_size(mut self, size: u32) -> Self {
39 self.pool_size = Some(size);
40 self
41 }
42
43 pub fn with_timeout(mut self, seconds: u32) -> Self {
45 self.timeout_seconds = Some(seconds);
46 self
47 }
48
49 pub fn get_pool_size(&self) -> u32 {
51 self.pool_size.unwrap_or(5)
52 }
53
54 pub fn get_timeout_seconds(&self) -> u32 {
56 self.timeout_seconds.unwrap_or(5)
57 }
58}
59
60pub struct ConnectionManager {
62 adapters: Arc<Mutex<HashMap<String, Arc<dyn DatabaseAdapter>>>>,
64}
65
66impl ConnectionManager {
67 pub fn new() -> Self {
69 Self {
70 adapters: Arc::new(Mutex::new(HashMap::new())),
71 }
72 }
73
74 pub async fn get_or_create_connection(
88 &self,
89 config: RemoteDatabaseConfig,
90 ) -> Result<Arc<dyn DatabaseAdapter>> {
91 {
93 let adapters = self.adapters.lock().map_err(|e| FraiseQLError::Internal {
94 message: format!("Connection cache lock error: {}", e),
95 source: None,
96 })?;
97
98 if let Some(adapter) = adapters.get(&config.connection_string) {
99 return Ok(Arc::clone(adapter));
100 }
101 }
102
103 Err(FraiseQLError::Internal {
107 message:
108 "Direct database connection creation requires database-specific implementation"
109 .to_string(),
110 source: None,
111 })
112 }
113
114 pub fn close_connection(&self, connection_string: &str) -> Result<()> {
116 let mut adapters = self.adapters.lock().map_err(|e| FraiseQLError::Internal {
117 message: format!("Connection cache lock error: {}", e),
118 source: None,
119 })?;
120
121 adapters.remove(connection_string);
122 Ok(())
123 }
124
125 pub fn close_all(&self) -> Result<()> {
127 let mut adapters = self.adapters.lock().map_err(|e| FraiseQLError::Internal {
128 message: format!("Connection cache lock error: {}", e),
129 source: None,
130 })?;
131
132 adapters.clear();
133 Ok(())
134 }
135
136 pub fn connection_count(&self) -> Result<usize> {
138 let adapters = self.adapters.lock().map_err(|e| FraiseQLError::Internal {
139 message: format!("Connection cache lock error: {}", e),
140 source: None,
141 })?;
142
143 Ok(adapters.len())
144 }
145}
146
147impl Default for ConnectionManager {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_remote_database_config_defaults() {
159 let config = RemoteDatabaseConfig::new("postgresql://localhost/db");
160 assert_eq!(config.get_pool_size(), 5);
161 assert_eq!(config.get_timeout_seconds(), 5);
162 }
163
164 #[test]
165 fn test_remote_database_config_custom() {
166 let config = RemoteDatabaseConfig::new("postgresql://localhost/db")
167 .with_pool_size(10)
168 .with_timeout(30);
169
170 assert_eq!(config.get_pool_size(), 10);
171 assert_eq!(config.get_timeout_seconds(), 30);
172 }
173
174 #[test]
175 fn test_connection_manager_creation() {
176 let _manager = ConnectionManager::new();
177 }
179
180 #[test]
181 fn test_connection_manager_default() {
182 let _manager = ConnectionManager::default();
183 }
185
186 #[test]
187 fn test_connection_count_empty() {
188 let manager = ConnectionManager::new();
189 assert_eq!(manager.connection_count().unwrap(), 0);
190 }
191
192 #[test]
193 fn test_close_all() {
194 let manager = ConnectionManager::new();
195 assert!(manager.close_all().is_ok());
197 }
198}