1use std::{fmt, str::FromStr};
2
3use config::{Config, Environment, File};
4use serde::{Deserialize, Serialize};
5
6use crate::DatabaseType;
7
8#[derive(Serialize, Deserialize, Debug, Clone)]
9pub struct DatabaseConfig {
10 pub name: String,
11 #[serde(rename = "type")]
12 pub db_type: DatabaseType,
13 pub conn_string: String,
14}
15
16#[derive(Serialize, Deserialize, Debug, Clone)]
17pub struct AppConfig {
18 pub server_addr: String,
19 #[serde(default)] pub databases: Vec<DatabaseConfig>,
21 pub jwt_secret: String,
22 pub allowed_origin: String,
23}
24
25impl AppConfig {
26 pub fn load() -> Result<Self, anyhow::Error> {
27 let config = Config::builder()
29 .add_source(File::with_name("config/default"))
30 .add_source(File::with_name("config/development").required(false))
31 .add_source(Environment::with_prefix("APP").separator("__"))
32 .build()?;
33
34 let app_config: AppConfig = config.try_deserialize()?;
35 Ok(app_config)
36 }
37}
38
39impl fmt::Display for DatabaseType {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 DatabaseType::Postgres => write!(f, "postgres"),
43 DatabaseType::Mysql => write!(f, "mysql"),
44 }
45 }
46}
47
48impl FromStr for DatabaseType {
49 type Err = anyhow::Error;
50
51 fn from_str(s: &str) -> Result<Self, Self::Err> {
52 match s.to_lowercase().as_str() {
53 "postgres" | "postgresql" => Ok(DatabaseType::Postgres),
54 "mysql" | "mariadb" => Ok(DatabaseType::Mysql),
55 _ => Err(anyhow::anyhow!("Invalid database type: {}", s)),
56 }
57 }
58}