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}