use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use crate::cli::DatabaseType;
use crate::error::{CliError, CliResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuthKitConfig {
pub database: DatabaseConfig,
#[serde(default)]
pub features: FeaturesConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
#[serde(rename = "type")]
pub db_type: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FeaturesConfig {
#[serde(default = "default_true")]
pub email_password: bool,
#[serde(default)]
pub email_verification: bool,
}
fn default_true() -> bool {
true
}
impl AuthKitConfig {
pub fn load<P: AsRef<Path>>(path: P) -> CliResult<Self> {
let path = path.as_ref();
if !path.exists() {
return Err(CliError::ConfigNotFound(path.display().to_string()));
}
let content = fs::read_to_string(path)?;
let config: AuthKitConfig =
toml::from_str(&content).map_err(|e| CliError::ConfigParse(e.to_string()))?;
config.validate()?;
Ok(config)
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> CliResult<()> {
let content =
toml::to_string_pretty(self).map_err(|e| CliError::ConfigParse(e.to_string()))?;
fs::write(path, content)?;
Ok(())
}
pub fn default_config(db_type: DatabaseType) -> Self {
Self {
database: DatabaseConfig {
db_type: db_type.to_string(),
},
features: FeaturesConfig {
email_password: true,
email_verification: false,
},
}
}
pub fn validate(&self) -> CliResult<()> {
match self.database.db_type.as_str() {
"sqlite" | "postgres" => {}
other => {
return Err(CliError::ConfigParse(format!(
"Invalid database type '{}'. Must be 'sqlite' or 'postgres'.",
other
)));
}
}
if !self.features.email_password {
return Err(CliError::ConfigParse(
"email_password feature must be enabled (it is the base feature)".to_string(),
));
}
Ok(())
}
pub fn database_type(&self) -> CliResult<DatabaseType> {
match self.database.db_type.as_str() {
"sqlite" => Ok(DatabaseType::Sqlite),
"postgres" => Ok(DatabaseType::Postgres),
other => Err(CliError::ConfigParse(format!(
"Invalid database type '{}'",
other
))),
}
}
pub fn enabled_features(&self) -> Vec<Feature> {
let mut features = Vec::new();
if self.features.email_password {
features.push(Feature::EmailPassword);
}
if self.features.email_verification {
features.push(Feature::EmailVerification);
}
features
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Feature {
EmailPassword,
EmailVerification,
}
impl Feature {
pub fn migration_name(&self) -> &'static str {
match self {
Feature::EmailPassword => "base",
Feature::EmailVerification => "email_verification",
}
}
pub fn display_name(&self) -> &'static str {
match self {
Feature::EmailPassword => "Email/Password Authentication",
Feature::EmailVerification => "Email Verification",
}
}
pub fn version(&self) -> u32 {
match self {
Feature::EmailPassword => 1,
Feature::EmailVerification => 2,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = AuthKitConfig::default_config(DatabaseType::Postgres);
assert_eq!(config.database.db_type, "postgres");
assert!(config.features.email_password);
assert!(!config.features.email_verification);
}
#[test]
fn test_enabled_features() {
let mut config = AuthKitConfig::default_config(DatabaseType::Postgres);
config.features.email_verification = true;
let features = config.enabled_features();
assert_eq!(features.len(), 2);
assert_eq!(features[0], Feature::EmailPassword);
assert_eq!(features[1], Feature::EmailVerification);
}
}