use crate::client::backend::BackendType;
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::time::Duration;
const REDACTED_SECRET: &str = "<redacted>";
fn debug_secret_option(value: &Option<String>) -> Option<&'static str> {
value.as_ref().map(|_| REDACTED_SECRET)
}
#[derive(Clone, Default)]
pub struct D1SessionConfig {
pub default_session_mode: Option<String>,
pub bookmark: Option<String>,
pub retry_writes: bool,
}
impl Debug for D1SessionConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("D1SessionConfig")
.field("default_session_mode", &self.default_session_mode)
.field("bookmark", &self.bookmark)
.field("retry_writes", &self.retry_writes)
.finish()
}
}
#[derive(Clone)]
pub struct ClientConfig {
pub backend_type: BackendType,
pub client_name: Option<String>,
pub connection: ConnectionConfig,
pub pool: PoolConfig,
pub health: HealthConfig,
pub d1: D1SessionConfig,
}
impl ClientConfig {
pub fn new(
backend_type: BackendType,
client_name: Option<String>,
connection: ConnectionConfig,
pool: PoolConfig,
health: HealthConfig,
d1: D1SessionConfig,
) -> Self {
Self {
backend_type,
client_name,
connection,
pool,
health,
d1,
}
}
}
impl Debug for ClientConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("ClientConfig")
.field("backend_type", &self.backend_type)
.field("client_name", &self.client_name)
.field("connection", &self.connection)
.field("pool", &self.pool)
.field("health", &self.health)
.field("d1", &self.d1)
.finish()
}
}
#[derive(Clone)]
pub struct ConnectionConfig {
pub url: String,
pub key: Option<String>,
pub ssl: bool,
pub port: Option<u16>,
pub database: Option<String>,
}
impl ConnectionConfig {
pub fn new(url: impl Into<String>) -> Self {
Self {
url: url.into(),
key: None,
ssl: true,
port: None,
database: None,
}
}
pub fn with_key(mut self, key: impl Into<String>) -> Self {
self.key = Some(key.into());
self
}
}
impl Debug for ConnectionConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("ConnectionConfig")
.field("url", &self.url)
.field("key", &debug_secret_option(&self.key))
.field("ssl", &self.ssl)
.field("port", &self.port)
.field("database", &self.database)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct PoolConfig {
pub max_connections: u32,
pub min_connections: u32,
pub connection_timeout: Duration,
pub idle_timeout: Duration,
}
impl Default for PoolConfig {
fn default() -> Self {
Self {
max_connections: 50,
min_connections: 0,
connection_timeout: Duration::from_secs(5),
idle_timeout: Duration::from_secs(300),
}
}
}
#[derive(Clone, Debug)]
pub struct HealthConfig {
pub enabled: bool,
pub check_interval: Duration,
pub circuit_breaker_threshold: u32,
pub circuit_breaker_timeout: Duration,
}
impl Default for HealthConfig {
fn default() -> Self {
Self {
enabled: true,
check_interval: Duration::from_secs(30),
circuit_breaker_threshold: 5,
circuit_breaker_timeout: Duration::from_secs(60),
}
}
}
#[cfg(test)]
mod tests {
use super::{ClientConfig, ConnectionConfig, D1SessionConfig, HealthConfig, PoolConfig};
use crate::client::backend::BackendType;
#[test]
fn connection_config_debug_redacts_key() {
let debug = format!(
"{:?}",
ConnectionConfig::new("http://localhost:8080").with_key("super-secret")
);
assert!(debug.contains("<redacted>"));
assert!(!debug.contains("super-secret"));
}
#[test]
fn client_config_debug_redacts_nested_key() {
let debug = format!(
"{:?}",
ClientConfig::new(
BackendType::Native,
Some("reporting".to_string()),
ConnectionConfig::new("http://localhost:8080").with_key("super-secret"),
PoolConfig::default(),
HealthConfig::default(),
D1SessionConfig::default(),
)
);
assert!(debug.contains("<redacted>"));
assert!(!debug.contains("super-secret"));
}
}