use super::{Backend, DbError};
#[derive(Debug, Clone)]
pub struct DbConfig {
pub host: String,
pub port: u16,
pub user: String,
pub password: String,
pub database: String,
pub pool_size: u32,
pub backend: Backend,
}
impl DbConfig {
pub fn from_env() -> Result<Self, DbError> {
let backend = match std::env::var("RWS_DB_BACKEND") {
Ok(s) => Backend::parse(&s)?,
Err(_) => Backend::unambiguous_default().ok_or_else(|| {
DbError::new(
"RWS_DB_BACKEND must be set to \"sqlite\", \"postgres\", or \"mysql\" \
when more than one model-* feature is compiled in",
)
})?,
};
let host = std::env::var("RWS_DB_HOST").unwrap_or_else(|_| "localhost".into());
let port = std::env::var("RWS_DB_PORT")
.unwrap_or_else(|_| "5432".into())
.parse::<u16>()
.map_err(|e| DbError::new(format!("RWS_DB_PORT: {}", e)))?;
let user = std::env::var("RWS_DB_USER").unwrap_or_default();
let password = std::env::var("RWS_DB_PASSWORD").unwrap_or_default();
let database = std::env::var("RWS_DB_NAME")
.map_err(|_| DbError::new("RWS_DB_NAME environment variable is required"))?;
let pool_size = std::env::var("RWS_DB_POOL_SIZE")
.unwrap_or_else(|_| "10".into())
.parse::<u32>()
.map_err(|e| DbError::new(format!("RWS_DB_POOL_SIZE: {}", e)))?;
Ok(DbConfig { host, port, user, password, database, pool_size, backend })
}
#[doc(hidden)]
pub fn to_url(&self) -> String {
match self.backend {
#[cfg(feature = "model-sqlite")]
Backend::Sqlite => format!("sqlite:{}", self.database),
#[cfg(feature = "model-postgres")]
Backend::Postgres => format!(
"postgres://{}:{}@{}:{}/{}",
self.user, self.password, self.host, self.port, self.database
),
#[cfg(feature = "model-mysql")]
Backend::MySql => format!(
"mysql://{}:{}@{}:{}/{}",
self.user, self.password, self.host, self.port, self.database
),
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct DbTls {
pub mode: DbSslMode,
pub root_cert: Option<String>,
pub client_cert: Option<String>,
pub client_key: Option<String>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum DbSslMode {
Disable,
#[default]
Prefer,
Require,
VerifyCa,
VerifyFull,
}
impl DbSslMode {
pub fn parse(value: &str) -> Result<DbSslMode, DbError> {
Ok(match value.trim().to_ascii_lowercase().replace('_', "-").as_str() {
"disable" | "disabled" => DbSslMode::Disable,
"" | "prefer" | "preferred" => DbSslMode::Prefer,
"require" | "required" => DbSslMode::Require,
"verify-ca" => DbSslMode::VerifyCa,
"verify-full" | "verify-identity" => DbSslMode::VerifyFull,
other => {
return Err(DbError::new(format!(
"RWS_DB_SSL_MODE: {other:?} is not disable, prefer, require, verify-ca or verify-full"
)))
}
})
}
}
impl DbTls {
pub fn from_env() -> Result<DbTls, DbError> {
let get = |name: &str| std::env::var(name).ok().map(|v| v.trim().to_string()).filter(|v| !v.is_empty());
let tls = DbTls {
mode: DbSslMode::parse(&get("RWS_DB_SSL_MODE").unwrap_or_default())?,
root_cert: get("RWS_DB_SSL_ROOT_CERT"),
client_cert: get("RWS_DB_SSL_CLIENT_CERT"),
client_key: get("RWS_DB_SSL_CLIENT_KEY"),
};
if tls.client_cert.is_some() != tls.client_key.is_some() {
return Err(DbError::new("RWS_DB_SSL_CLIENT_CERT and RWS_DB_SSL_CLIENT_KEY go together"));
}
Ok(tls)
}
}