use crate::error::{sanitize_dsn, GraphError};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
#[derive(Debug, Clone)]
pub struct GraphConfig {
pub dsn: String,
pub connect_timeout_secs: u64,
pub query_timeout_secs: u64,
pub max_pool_size: usize,
}
impl GraphConfig {
pub fn new(dsn: &str) -> Self {
Self {
dsn: dsn.to_string(),
connect_timeout_secs: 10,
query_timeout_secs: 30,
max_pool_size: 10,
}
}
pub fn with_connect_timeout(mut self, secs: u64) -> Self {
self.connect_timeout_secs = secs;
self
}
pub fn with_query_timeout(mut self, secs: u64) -> Self {
self.query_timeout_secs = secs;
self
}
pub fn with_pool_size(mut self, size: usize) -> Self {
self.max_pool_size = size;
self
}
pub fn sanitized_dsn(&self) -> String {
sanitize_dsn(&self.dsn)
}
}
pub struct GraphConnection {
config: GraphConfig,
connected: bool,
}
impl GraphConnection {
pub fn new(config: GraphConfig) -> Self {
Self {
config,
connected: false,
}
}
pub fn config(&self) -> &GraphConfig {
&self.config
}
pub fn is_connected(&self) -> bool {
self.connected
}
pub fn connect(&mut self) -> Result<(), GraphError> {
if self.config.dsn.is_empty() {
return Err(GraphError::ConnectionError("empty DSN".into()));
}
if !self.config.dsn.starts_with("neo4j://") && !self.config.dsn.starts_with("bolt://") {
return Err(GraphError::ConnectionError(format!(
"invalid DSN scheme: {}",
self.config.sanitized_dsn()
)));
}
self.connected = true;
Ok(())
}
pub fn disconnect(&mut self) {
self.connected = false;
}
}
pub struct GraphPool {
config: GraphConfig,
connections: Arc<Mutex<Vec<GraphConnection>>>,
}
impl GraphPool {
pub fn new(config: GraphConfig) -> Self {
Self {
config,
connections: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn config(&self) -> &GraphConfig {
&self.config
}
pub async fn acquire(&self) -> Result<GraphConnection, GraphError> {
let mut conns = self.connections.lock().await;
if let Some(conn) = conns.pop() {
return Ok(conn);
}
if conns.len() >= self.config.max_pool_size {
return Err(GraphError::ConnectionError(format!(
"pool exhausted (max={}), DSN: {}",
self.config.max_pool_size,
self.config.sanitized_dsn()
)));
}
let mut conn = GraphConnection::new(self.config.clone());
conn.connect()?;
Ok(conn)
}
pub async fn release(&self, conn: GraphConnection) {
let mut conns = self.connections.lock().await;
conns.push(conn);
}
pub async fn size(&self) -> usize {
self.connections.lock().await.len()
}
}
impl GraphConfig {
pub fn connect_timeout(&self) -> Duration {
Duration::from_secs(self.connect_timeout_secs)
}
pub fn query_timeout(&self) -> Duration {
Duration::from_secs(self.query_timeout_secs)
}
}