botcat_capoo/
config.rs

1use std::str::FromStr;
2
3use config::{Config, File};
4use serde::{Deserialize, Serialize};
5
6use crate::error::Error;
7
8#[derive(PartialEq)]
9pub enum AppEnv {
10    Development,
11    Test,
12    Production,
13}
14
15impl AppEnv {
16    pub fn as_str(&self) -> &'static str {
17        match self {
18            Self::Development => "dev",
19            Self::Test => "test",
20            Self::Production => "prod",
21        }
22    }
23
24    pub fn is_development(&self) -> bool {
25        *self == Self::Development
26    }
27
28    pub fn is_test(&self) -> bool {
29        *self == Self::Test
30    }
31
32    pub fn is_production(&self) -> bool {
33        *self == Self::Production
34    }
35}
36
37impl FromStr for AppEnv {
38    type Err = Error;
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        match s {
42            "development" | "dev" | "develop" => Ok(Self::Development),
43            "test" => Ok(Self::Test),
44            "production" | "prod" | "product" => Ok(Self::Production),
45            _ => Err(Self::Err::Other {
46                desc: format!("Invalid environment: {s}"),
47            }),
48        }
49    }
50}
51
52#[derive(Clone, Debug, Deserialize, Serialize)]
53pub struct BotcatCapooConfig {
54    pub host: String,
55    pub port: u16,
56    pub owner: u64,
57    pub id: u64,
58    pub database: DatabaseConfig,
59}
60
61#[derive(Clone, Debug, Deserialize, Serialize)]
62pub struct NapcatConfig {
63    pub api_url: String,
64    pub port: u16,
65}
66
67#[derive(Clone, Debug, Deserialize, Serialize)]
68pub enum DatabaseType {
69    Postgres,
70    Sqlite,
71    MySql,
72}
73
74impl FromStr for DatabaseType {
75    type Err = Error;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        match s.to_lowercase().as_str() {
79            "postgres" | "postgresql" | "psql" | "pgsql" | "pg" => Ok(Self::Postgres),
80            "sqlite" | "sqlite3" => Ok(Self::Sqlite),
81            "mysql" => Ok(Self::MySql),
82            _ => Err(Self::Err::Other {
83                desc: format!("Invalid database type: {s}"),
84            }),
85        }
86    }
87}
88
89#[derive(Clone, Debug, Deserialize, Serialize)]
90pub struct DatabaseConfig {
91    pub db_type: String,
92    pub host: Option<String>,
93    pub port: Option<u16>,
94    pub username: Option<String>,
95    pub password: Option<String>,
96    pub database: String,
97    pub max_connections: Option<u32>,
98}
99
100#[derive(Clone, Debug, Deserialize, Serialize)]
101pub struct SecretConfig {
102    pub http_client_token: String,
103    pub http_server_token: String,
104}
105
106#[derive(Clone, Debug, Deserialize, Serialize)]
107pub struct AppConfig {
108    pub botcat_capoo: BotcatCapooConfig,
109    pub napcat: NapcatConfig,
110    pub secret: SecretConfig,
111}
112
113impl AppConfig {
114    pub fn init() -> Result<AppConfig, Error> {
115        let env = std::env::var("BOTCAT_CAPOO_ENV").unwrap_or_else(|_| "dev".to_string());
116        let env = AppEnv::from_str(&env)
117            .unwrap_or(AppEnv::Development)
118            .as_str();
119
120        let settings = Config::builder()
121            .add_source(File::with_name(&format!("config/{env}")))
122            .add_source(File::with_name(&format!("secret/{env}")))
123            .build()?;
124        settings.try_deserialize::<AppConfig>().map_err(Error::from)
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    #[ignore = "Requires secret files which are not included in the repository"]
134    fn test_config_initialization() {
135        let config = AppConfig::init();
136        assert!(config.is_ok());
137    }
138}