1use std::path::PathBuf;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
8pub struct ServerConfig {
9 pub http_port: u16,
11
12 pub ws_port: Option<u16>,
14
15 pub grpc_port: Option<u16>,
17
18 pub admin_port: Option<u16>,
20
21 pub metrics_port: Option<u16>,
23
24 pub spec_file: Option<PathBuf>,
26
27 pub workspace_dir: Option<PathBuf>,
29
30 pub profile: Option<String>,
32
33 pub enable_admin: bool,
35
36 pub enable_metrics: bool,
38
39 pub extra_args: Vec<String>,
41
42 pub health_timeout: Duration,
44
45 pub health_interval: Duration,
47
48 pub working_dir: Option<PathBuf>,
50
51 pub env_vars: Vec<(String, String)>,
53
54 pub binary_path: Option<PathBuf>,
56}
57
58impl Default for ServerConfig {
59 fn default() -> Self {
60 Self {
61 http_port: 0, ws_port: None,
63 grpc_port: None,
64 admin_port: None,
65 metrics_port: None,
66 spec_file: None,
67 workspace_dir: None,
68 profile: None,
69 enable_admin: false,
70 enable_metrics: false,
71 extra_args: Vec::new(),
72 health_timeout: Duration::from_secs(30),
73 health_interval: Duration::from_millis(100),
74 working_dir: None,
75 env_vars: Vec::new(),
76 binary_path: None,
77 }
78 }
79}
80
81impl ServerConfig {
82 pub fn builder() -> ServerConfigBuilder {
84 ServerConfigBuilder::default()
85 }
86}
87
88#[derive(Debug, Default)]
90pub struct ServerConfigBuilder {
91 config: ServerConfig,
92}
93
94impl ServerConfigBuilder {
95 pub fn http_port(mut self, port: u16) -> Self {
97 self.config.http_port = port;
98 self
99 }
100
101 pub fn ws_port(mut self, port: u16) -> Self {
103 self.config.ws_port = Some(port);
104 self
105 }
106
107 pub fn grpc_port(mut self, port: u16) -> Self {
109 self.config.grpc_port = Some(port);
110 self
111 }
112
113 pub fn admin_port(mut self, port: u16) -> Self {
115 self.config.admin_port = Some(port);
116 self
117 }
118
119 pub fn metrics_port(mut self, port: u16) -> Self {
121 self.config.metrics_port = Some(port);
122 self
123 }
124
125 pub fn spec_file<P: Into<PathBuf>>(mut self, path: P) -> Self {
127 self.config.spec_file = Some(path.into());
128 self
129 }
130
131 pub fn workspace_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
133 self.config.workspace_dir = Some(path.into());
134 self
135 }
136
137 pub fn profile<S: Into<String>>(mut self, profile: S) -> Self {
139 self.config.profile = Some(profile.into());
140 self
141 }
142
143 pub fn enable_admin(mut self, enable: bool) -> Self {
145 self.config.enable_admin = enable;
146 self
147 }
148
149 pub fn enable_metrics(mut self, enable: bool) -> Self {
151 self.config.enable_metrics = enable;
152 self
153 }
154
155 pub fn extra_arg<S: Into<String>>(mut self, arg: S) -> Self {
157 self.config.extra_args.push(arg.into());
158 self
159 }
160
161 pub fn extra_args<I, S>(mut self, args: I) -> Self
163 where
164 I: IntoIterator<Item = S>,
165 S: Into<String>,
166 {
167 self.config.extra_args.extend(args.into_iter().map(Into::into));
168 self
169 }
170
171 pub fn health_timeout(mut self, timeout: Duration) -> Self {
173 self.config.health_timeout = timeout;
174 self
175 }
176
177 pub fn health_interval(mut self, interval: Duration) -> Self {
179 self.config.health_interval = interval;
180 self
181 }
182
183 pub fn working_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
185 self.config.working_dir = Some(path.into());
186 self
187 }
188
189 pub fn env_var<K, V>(mut self, key: K, value: V) -> Self
191 where
192 K: Into<String>,
193 V: Into<String>,
194 {
195 self.config.env_vars.push((key.into(), value.into()));
196 self
197 }
198
199 pub fn binary_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
201 self.config.binary_path = Some(path.into());
202 self
203 }
204
205 pub fn build(self) -> ServerConfig {
207 self.config
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn test_default_config() {
217 let config = ServerConfig::default();
218 assert_eq!(config.http_port, 0);
219 assert!(config.ws_port.is_none());
220 assert!(config.grpc_port.is_none());
221 assert!(config.admin_port.is_none());
222 assert!(config.metrics_port.is_none());
223 assert!(config.spec_file.is_none());
224 assert!(config.workspace_dir.is_none());
225 assert!(config.profile.is_none());
226 assert!(!config.enable_admin);
227 assert!(!config.enable_metrics);
228 assert!(config.extra_args.is_empty());
229 assert_eq!(config.health_timeout, Duration::from_secs(30));
230 assert_eq!(config.health_interval, Duration::from_millis(100));
231 assert!(config.working_dir.is_none());
232 assert!(config.env_vars.is_empty());
233 assert!(config.binary_path.is_none());
234 }
235
236 #[test]
237 fn test_builder() {
238 let config = ServerConfig::builder()
239 .http_port(3000)
240 .ws_port(3001)
241 .grpc_port(3002)
242 .admin_port(3003)
243 .enable_admin(true)
244 .enable_metrics(true)
245 .profile("test")
246 .extra_arg("--verbose")
247 .build();
248
249 assert_eq!(config.http_port, 3000);
250 assert_eq!(config.ws_port, Some(3001));
251 assert_eq!(config.grpc_port, Some(3002));
252 assert_eq!(config.admin_port, Some(3003));
253 assert!(config.enable_admin);
254 assert!(config.enable_metrics);
255 assert_eq!(config.profile, Some("test".to_string()));
256 assert_eq!(config.extra_args, vec!["--verbose"]);
257 }
258
259 #[test]
260 fn test_builder_metrics_port() {
261 let config = ServerConfig::builder().metrics_port(9090).build();
262 assert_eq!(config.metrics_port, Some(9090));
263 }
264
265 #[test]
266 fn test_builder_spec_file() {
267 let config = ServerConfig::builder().spec_file("/path/to/spec.yaml").build();
268 assert_eq!(config.spec_file, Some(PathBuf::from("/path/to/spec.yaml")));
269 }
270
271 #[test]
272 fn test_builder_workspace_dir() {
273 let config = ServerConfig::builder().workspace_dir("/path/to/workspace").build();
274 assert_eq!(config.workspace_dir, Some(PathBuf::from("/path/to/workspace")));
275 }
276
277 #[test]
278 fn test_builder_extra_args() {
279 let config = ServerConfig::builder().extra_args(vec!["--verbose", "--debug"]).build();
280 assert_eq!(config.extra_args, vec!["--verbose", "--debug"]);
281 }
282
283 #[test]
284 fn test_builder_extra_args_combined() {
285 let config = ServerConfig::builder()
286 .extra_arg("--first")
287 .extra_args(vec!["--second", "--third"])
288 .extra_arg("--fourth")
289 .build();
290 assert_eq!(config.extra_args, vec!["--first", "--second", "--third", "--fourth"]);
291 }
292
293 #[test]
294 fn test_builder_health_timeout() {
295 let config = ServerConfig::builder().health_timeout(Duration::from_secs(60)).build();
296 assert_eq!(config.health_timeout, Duration::from_secs(60));
297 }
298
299 #[test]
300 fn test_builder_health_interval() {
301 let config = ServerConfig::builder().health_interval(Duration::from_millis(500)).build();
302 assert_eq!(config.health_interval, Duration::from_millis(500));
303 }
304
305 #[test]
306 fn test_builder_working_dir() {
307 let config = ServerConfig::builder().working_dir("/tmp/test").build();
308 assert_eq!(config.working_dir, Some(PathBuf::from("/tmp/test")));
309 }
310
311 #[test]
312 fn test_builder_env_var() {
313 let config = ServerConfig::builder()
314 .env_var("RUST_LOG", "debug")
315 .env_var("MY_VAR", "value")
316 .build();
317 assert_eq!(config.env_vars.len(), 2);
318 assert!(config.env_vars.contains(&("RUST_LOG".to_string(), "debug".to_string())));
319 assert!(config.env_vars.contains(&("MY_VAR".to_string(), "value".to_string())));
320 }
321
322 #[test]
323 fn test_builder_binary_path() {
324 let config = ServerConfig::builder().binary_path("/usr/local/bin/mockforge").build();
325 assert_eq!(config.binary_path, Some(PathBuf::from("/usr/local/bin/mockforge")));
326 }
327
328 #[test]
329 fn test_config_clone() {
330 let config = ServerConfig::builder().http_port(3000).profile("test").build();
331
332 let cloned = config.clone();
333 assert_eq!(config.http_port, cloned.http_port);
334 assert_eq!(config.profile, cloned.profile);
335 }
336
337 #[test]
338 fn test_config_debug() {
339 let config = ServerConfig::default();
340 let debug = format!("{:?}", config);
341 assert!(debug.contains("ServerConfig"));
342 }
343
344 #[test]
345 fn test_builder_debug() {
346 let builder = ServerConfigBuilder::default();
347 let debug = format!("{:?}", builder);
348 assert!(debug.contains("ServerConfigBuilder"));
349 }
350
351 #[test]
352 fn test_builder_full_chain() {
353 let config = ServerConfig::builder()
354 .http_port(3000)
355 .ws_port(3001)
356 .grpc_port(3002)
357 .admin_port(3003)
358 .metrics_port(9090)
359 .spec_file("/spec.yaml")
360 .workspace_dir("/workspace")
361 .profile("production")
362 .enable_admin(true)
363 .enable_metrics(true)
364 .extra_arg("--verbose")
365 .health_timeout(Duration::from_secs(60))
366 .health_interval(Duration::from_millis(200))
367 .working_dir("/working")
368 .env_var("KEY", "VALUE")
369 .binary_path("/bin/mockforge")
370 .build();
371
372 assert_eq!(config.http_port, 3000);
373 assert_eq!(config.ws_port, Some(3001));
374 assert_eq!(config.grpc_port, Some(3002));
375 assert_eq!(config.admin_port, Some(3003));
376 assert_eq!(config.metrics_port, Some(9090));
377 assert_eq!(config.spec_file, Some(PathBuf::from("/spec.yaml")));
378 assert_eq!(config.workspace_dir, Some(PathBuf::from("/workspace")));
379 assert_eq!(config.profile, Some("production".to_string()));
380 assert!(config.enable_admin);
381 assert!(config.enable_metrics);
382 assert_eq!(config.health_timeout, Duration::from_secs(60));
383 assert_eq!(config.health_interval, Duration::from_millis(200));
384 assert_eq!(config.working_dir, Some(PathBuf::from("/working")));
385 assert_eq!(config.binary_path, Some(PathBuf::from("/bin/mockforge")));
386 }
387
388 #[test]
389 fn test_server_config_builder_method() {
390 let builder = ServerConfig::builder();
391 let config = builder.http_port(8080).build();
392 assert_eq!(config.http_port, 8080);
393 }
394}