1use std::time::Duration;
6
7#[derive(Debug, Clone)]
9pub struct Config {
10 pub base_url: String,
12
13 pub timeout: Duration,
15
16 pub retries: u32,
18
19 pub log_level: String,
21
22 pub deploy_strategy: DeployStrategy,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum DeployStrategy {
29 Remote,
31
32 Local,
34
35 Mock,
37}
38
39#[derive(Debug, Clone, Default)]
56pub struct ConfigBuilder {
57 base_url: Option<String>,
58 timeout: Option<Duration>,
59 retries: Option<u32>,
60 log_level: Option<String>,
61 deploy_strategy: Option<DeployStrategy>,
62}
63
64impl ConfigBuilder {
65 #[must_use]
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 #[must_use]
73 pub fn base_url(mut self, base_url: impl Into<String>) -> Self {
74 self.base_url = Some(base_url.into());
75 self
76 }
77
78 #[must_use]
80 pub fn timeout(mut self, timeout: Duration) -> Self {
81 self.timeout = Some(timeout);
82 self
83 }
84
85 #[must_use]
87 pub fn retries(mut self, retries: u32) -> Self {
88 self.retries = Some(retries);
89 self
90 }
91
92 #[must_use]
94 pub fn log_level(mut self, log_level: impl Into<String>) -> Self {
95 self.log_level = Some(log_level.into());
96 self
97 }
98
99 #[must_use]
101 pub fn deploy_strategy(mut self, strategy: DeployStrategy) -> Self {
102 self.deploy_strategy = Some(strategy);
103 self
104 }
105
106 #[must_use]
108 pub fn build(self) -> Config {
109 Config {
110 base_url: self.base_url.unwrap_or_else(|| {
111 std::env::var("SERVERLESS_BASE_URL")
112 .unwrap_or_else(|_| "http://localhost:3000".to_string())
113 }),
114 timeout: self.timeout.unwrap_or_else(|| {
115 Duration::from_millis(
116 std::env::var("SERVERLESS_TIMEOUT")
117 .unwrap_or_else(|_| "30000".to_string())
118 .parse()
119 .unwrap_or(30_000),
120 )
121 }),
122 retries: self.retries.unwrap_or_else(|| {
123 std::env::var("SERVERLESS_RETRIES")
124 .unwrap_or_else(|_| "3".to_string())
125 .parse()
126 .unwrap_or(3)
127 }),
128 log_level: self.log_level.unwrap_or_else(|| {
129 std::env::var("SERVERLESS_LOG_LEVEL").unwrap_or_else(|_| "info".to_string())
130 }),
131 deploy_strategy: self
132 .deploy_strategy
133 .unwrap_or_else(Config::determine_deploy_strategy),
134 }
135 }
136}
137
138impl Config {
139 #[must_use]
141 pub fn builder() -> ConfigBuilder {
142 ConfigBuilder::new()
143 }
144
145 #[must_use]
154 pub fn from_env() -> Self {
155 let deploy_strategy = Self::determine_deploy_strategy();
156
157 Self {
158 base_url: std::env::var("SERVERLESS_BASE_URL")
159 .unwrap_or_else(|_| "http://localhost:3000".to_string()),
160 timeout: Duration::from_millis(
161 std::env::var("SERVERLESS_TIMEOUT")
162 .unwrap_or_else(|_| "30000".to_string())
163 .parse()
164 .unwrap_or(30_000),
165 ),
166 retries: std::env::var("SERVERLESS_RETRIES")
167 .unwrap_or_else(|_| "3".to_string())
168 .parse()
169 .unwrap_or(3),
170 log_level: std::env::var("SERVERLESS_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()),
171 deploy_strategy,
172 }
173 }
174
175 fn determine_deploy_strategy() -> DeployStrategy {
177 if cfg!(feature = "mock_server") {
178 DeployStrategy::Mock
179 } else if cfg!(feature = "local_call") {
180 DeployStrategy::Local
181 } else {
182 DeployStrategy::Remote
183 }
184 }
185
186 #[must_use]
188 pub fn deploy_strategy(&self) -> &DeployStrategy {
189 &self.deploy_strategy
190 }
191
192 #[must_use]
194 pub fn base_url(&self) -> &str {
195 &self.base_url
196 }
197
198 #[must_use]
200 pub fn timeout(&self) -> Duration {
201 self.timeout
202 }
203
204 #[must_use]
206 pub fn retries(&self) -> u32 {
207 self.retries
208 }
209
210 #[must_use]
212 pub fn log_level(&self) -> &str {
213 &self.log_level
214 }
215
216 pub fn init_logging(&self) {
222 use tracing_subscriber::{EnvFilter, fmt};
223
224 let env_filter =
225 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(&self.log_level));
226
227 fmt().with_env_filter(env_filter).init();
228 }
229}
230
231impl Default for Config {
232 fn default() -> Self {
233 Self::from_env()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_config_default() {
243 let config = Config::default();
244 assert_eq!(config.base_url, "http://localhost:3000");
245 assert_eq!(config.timeout, Duration::from_millis(30_000));
246 assert_eq!(config.retries, 3);
247 assert_eq!(config.log_level, "info");
248 }
249
250 #[test]
251 fn test_deploy_strategy_remote() {
252 if !cfg!(feature = "mock_server") && !cfg!(feature = "local_call") {
254 let config = Config::default();
255 assert_eq!(*config.deploy_strategy(), DeployStrategy::Remote);
256 }
257 }
258
259 #[test]
260 fn test_config_builder() {
261 let config = Config::builder()
262 .base_url("http://localhost:8080")
263 .timeout(Duration::from_secs(60))
264 .retries(5)
265 .log_level("debug")
266 .build();
267
268 assert_eq!(config.base_url, "http://localhost:8080");
269 assert_eq!(config.timeout, Duration::from_secs(60));
270 assert_eq!(config.retries, 5);
271 assert_eq!(config.log_level, "debug");
272 }
273}