use serde::Deserialize;
use std::path::Path;
use crate::error::{CliError, Result};
#[derive(Debug, Deserialize, Default)]
pub struct GenerateConfig {
pub schema: Option<String>,
pub output: Option<String>,
pub targets: Option<Vec<String>>,
}
#[derive(Debug, Deserialize, Default)]
pub struct TenantConfig {
pub root: Option<String>,
}
impl TenantConfig {
pub fn root(&self) -> std::path::PathBuf {
std::path::PathBuf::from(self.root.as_deref().unwrap_or("data"))
}
}
#[derive(Debug, Deserialize, Default)]
pub struct AuthConfig {
#[serde(default)]
pub enabled: bool,
pub issuer: Option<String>,
pub audience: Option<String>,
pub tenant_claim: Option<String>,
pub jwks_url: Option<String>,
pub public_key_path: Option<String>,
pub algorithms: Option<Vec<String>>,
pub leeway_secs: Option<u64>,
}
impl AuthConfig {
pub fn env_exports(&self) -> Vec<(String, String)> {
if !self.enabled {
return Vec::new();
}
let mut out = Vec::new();
if let Some(path) = &self.public_key_path {
out.push(("FORGEDB_JWT_PUBKEY".to_string(), path.clone()));
}
if let Some(url) = &self.jwks_url {
out.push(("FORGEDB_JWKS_URL".to_string(), url.clone()));
}
if let Some(iss) = &self.issuer {
out.push(("FORGEDB_JWT_ISSUER".to_string(), iss.clone()));
}
if let Some(aud) = &self.audience {
out.push(("FORGEDB_JWT_AUDIENCE".to_string(), aud.clone()));
}
if let Some(claim) = &self.tenant_claim {
out.push(("FORGEDB_TENANT_CLAIM".to_string(), claim.clone()));
}
if let Some(first) = self.algorithms.as_ref().and_then(|a| a.first()) {
out.push(("FORGEDB_JWT_ALG".to_string(), first.clone()));
}
if let Some(leeway) = self.leeway_secs {
out.push(("FORGEDB_JWT_LEEWAY".to_string(), leeway.to_string()));
}
out
}
}
#[derive(Debug, Deserialize, Default)]
pub struct ForgeConfig {
#[serde(default)]
pub generate: GenerateConfig,
#[serde(default)]
pub tenant: TenantConfig,
#[serde(default)]
pub auth: AuthConfig,
}
pub fn load_config(path: Option<&str>) -> Result<ForgeConfig> {
match path {
Some(explicit_path) => {
let content = std::fs::read_to_string(explicit_path).map_err(|e| {
CliError::Config(format!(
"Cannot read config file '{}': {}",
explicit_path, e
))
})?;
toml::from_str(&content).map_err(|e| {
CliError::Config(format!(
"Invalid config file '{}': {}",
explicit_path, e
))
})
}
None => {
if Path::new("forgedb.toml").exists() {
let content = std::fs::read_to_string("forgedb.toml")
.map_err(|e| CliError::Config(format!("Cannot read forgedb.toml: {}", e)))?;
toml::from_str(&content)
.map_err(|e| CliError::Config(format!("Invalid forgedb.toml: {}", e)))
} else {
Ok(ForgeConfig::default())
}
}
}
}