1use serde::{Deserialize, Serialize};
2use std::net::SocketAddr;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Config {
7 pub server: ServerConfig,
9 pub log: LogConfig,
11 pub cors: CorsConfig,
13 #[serde(default)]
15 #[cfg(feature = "database")]
16 pub database: Option<DatabaseConfig>,
17 #[serde(default)]
19 #[cfg(feature = "redis")]
20 pub redis: Option<RedisConfig>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ServerConfig {
26 pub addr: String,
28 pub port: u16,
30 pub workers: Option<usize>,
32 #[serde(default)]
34 pub context_path: Option<String>,
35}
36
37impl ServerConfig {
38 pub fn socket_addr(&self) -> Result<SocketAddr, std::net::AddrParseError> {
40 format!("{}:{}", self.addr, self.port).parse()
41 }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct LogConfig {
47 pub level: String,
49 pub json: bool,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct CorsConfig {
56 pub allowed_origins: Vec<String>,
58 pub allowed_methods: Vec<String>,
60 pub allowed_headers: Vec<String>,
62 pub allow_credentials: bool,
64}
65
66#[cfg(feature = "database")]
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct DatabaseConfig {
70 pub url: String,
72 #[serde(default = "default_max_connections")]
74 pub max_connections: u32,
75 #[serde(default = "default_min_connections")]
77 pub min_connections: u32,
78}
79
80#[cfg(feature = "database")]
81fn default_max_connections() -> u32 {
82 100
83}
84
85#[cfg(feature = "database")]
86fn default_min_connections() -> u32 {
87 10
88}
89
90#[cfg(feature = "redis")]
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct RedisConfig {
94 pub url: String,
96 #[serde(default)]
98 pub password: Option<String>,
99 #[serde(default = "default_pool_size")]
101 pub pool_size: usize,
102}
103
104#[cfg(feature = "redis")]
105fn default_pool_size() -> usize {
106 10
107}
108
109impl Default for Config {
110 fn default() -> Self {
111 Self {
112 server: ServerConfig {
113 addr: "0.0.0.0".to_string(),
114 port: 3000,
115 workers: None,
116 context_path: None,
117 },
118 log: LogConfig {
119 level: "info".to_string(),
120 json: false,
121 },
122 cors: CorsConfig {
123 allowed_origins: vec!["*".to_string()],
124 allowed_methods: vec![
125 "GET".to_string(),
126 "POST".to_string(),
127 "PUT".to_string(),
128 "DELETE".to_string(),
129 "PATCH".to_string(),
130 "OPTIONS".to_string(),
131 ],
132 allowed_headers: vec!["*".to_string()],
133 allow_credentials: false,
135 },
136 #[cfg(feature = "database")]
137 database: None,
138 #[cfg(feature = "redis")]
139 redis: None,
140 }
141 }
142}
143
144impl Config {
145 pub fn from_env() -> Result<Self, config::ConfigError> {
164 dotenvy::dotenv().ok(); let builder = config::Config::builder()
168 .set_default("server.addr", "127.0.0.1")?
169 .set_default("server.port", 3000)?
170 .set_default("log.level", "info")?
171 .set_default("log.json", false)?
172 .set_default("cors.allowed_origins", vec!["*"])?
173 .set_default(
174 "cors.allowed_methods",
175 vec!["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
176 )?
177 .set_default("cors.allowed_headers", vec!["*"])?
178 .set_default("cors.allow_credentials", false)?
180 .add_source(config::Environment::with_prefix("APP").separator("__"));
182
183 builder.build()?.try_deserialize()
184 }
185}