use super::{Config, DatabaseConfig, JwtConfig, MultiTenantConfig};
#[cfg(feature = "admin-override")]
use crate::auth::admin_override::AdminOverrideConfig;
#[derive(Debug, Default)]
pub struct ConfigBuilder {
database: DatabaseConfig,
jwt: JwtConfig,
multi_tenant: MultiTenantConfig,
log_level: String,
#[cfg(feature = "admin-override")]
admin_override: Option<AdminOverrideConfig>,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self {
database: DatabaseConfig::default(),
jwt: JwtConfig::default(),
multi_tenant: MultiTenantConfig::default(),
log_level: "info".to_string(),
#[cfg(feature = "admin-override")]
admin_override: None,
}
}
pub fn database_host(mut self, host: impl Into<String>) -> Self {
self.database.host = host.into();
self
}
pub fn database_user(mut self, user: impl Into<String>) -> Self {
self.database.user = user.into();
self
}
pub fn database_password(mut self, password: impl Into<String>) -> Self {
self.database.password = password.into();
self
}
pub fn database_name(mut self, name: impl Into<String>) -> Self {
self.database.name = name.into();
self
}
pub fn database_port(mut self, port: u16) -> Self {
self.database.port = port;
self
}
pub fn database_url(mut self, url: &str) -> Self {
if let Some(rest) = url.strip_prefix("mysql://").or(url.strip_prefix("postgres://")) {
if let Some((auth, host_db)) = rest.split_once('@') {
if let Some((user, password)) = auth.split_once(':') {
self.database.user = user.to_string();
self.database.password = password.to_string();
}
if let Some((host_port, dbname)) = host_db.split_once('/') {
self.database.name = dbname.to_string();
if let Some((host, port)) = host_port.split_once(':') {
self.database.host = host.to_string();
if let Ok(p) = port.parse() {
self.database.port = p;
}
} else {
self.database.host = host_port.to_string();
}
}
}
}
self
}
pub fn jwt_secret(mut self, secret: impl Into<String>) -> Self {
self.jwt.secret = secret.into();
self
}
pub fn jwt_exp_days(mut self, days: i64) -> Self {
self.jwt.exp_days = days;
self
}
pub fn multi_tenant(mut self, enabled: bool) -> Self {
self.multi_tenant.enabled = enabled;
self
}
pub fn required_db_version(mut self, version: i32) -> Self {
self.multi_tenant.required_db_version = version;
self
}
pub fn tenant_db_password(mut self, password: impl Into<String>) -> Self {
self.multi_tenant.db_password = Some(password.into());
self
}
#[cfg(feature = "admin-override")]
pub fn admin_override_secret(mut self, secret: impl Into<String>) -> Self {
let secret = secret.into();
if let Some(ref mut config) = self.admin_override {
config.secret = secret;
} else {
self.admin_override = Some(AdminOverrideConfig::new(secret));
}
self
}
#[cfg(feature = "admin-override")]
pub fn admin_override_expiry_secs(mut self, secs: i64) -> Self {
if let Some(ref mut config) = self.admin_override {
config.expiry_secs = secs;
} else {
self.admin_override = Some(AdminOverrideConfig::new(String::new()).with_expiry_secs(secs));
}
self
}
pub fn log_level(mut self, level: impl Into<String>) -> Self {
self.log_level = level.into();
self
}
pub fn build(self) -> Result<Config, String> {
if self.jwt.secret.is_empty() {
return Err("JWT secret is required".to_string());
}
if self.database.host.is_empty() {
return Err("Database host is required".to_string());
}
Ok(Config {
database: self.database,
jwt: self.jwt,
multi_tenant: self.multi_tenant,
log_level: self.log_level,
#[cfg(feature = "admin-override")]
admin_override: self.admin_override,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder() {
let config = ConfigBuilder::new()
.database_host("localhost")
.database_user("root")
.database_password("secret")
.database_name("testdb")
.jwt_secret("test-secret")
.jwt_exp_days(14)
.multi_tenant(true)
.required_db_version(2)
.build()
.unwrap();
assert_eq!(config.database.host, "localhost");
assert_eq!(config.database.name, "testdb");
assert_eq!(config.jwt.exp_days, 14);
assert!(config.multi_tenant.enabled);
assert_eq!(config.multi_tenant.required_db_version, 2);
}
#[test]
fn test_database_url_parsing() {
let config = ConfigBuilder::new()
.database_url("mysql://myuser:mypass@myhost:3307/mydb")
.jwt_secret("secret")
.build()
.unwrap();
assert_eq!(config.database.user, "myuser");
assert_eq!(config.database.password, "mypass");
assert_eq!(config.database.host, "myhost");
assert_eq!(config.database.port, 3307);
assert_eq!(config.database.name, "mydb");
}
#[test]
fn test_missing_jwt_secret() {
let result = ConfigBuilder::new()
.database_host("localhost")
.build();
assert!(result.is_err());
assert!(result.unwrap_err().contains("JWT secret"));
}
}