1use clap::Parser;
9use hyper::StatusCode;
10use std::{net::IpAddr, path::PathBuf};
11
12#[cfg(feature = "directory-listing")]
13use crate::directory_listing::DirListFmt;
14use crate::Result;
15
16#[derive(Parser, Debug)]
18#[command(author, about, long_about)]
19pub struct General {
20 #[arg(long, short = 'a', default_value = "::", env = "SERVER_HOST")]
21 pub host: String,
23
24 #[arg(long, short = 'p', default_value = "80", env = "SERVER_PORT")]
25 pub port: u16,
27
28 #[cfg_attr(
29 feature = "http2",
30 arg(
31 long,
32 short = 'f',
33 env = "SERVER_LISTEN_FD",
34 conflicts_with_all(&["host", "port", "https_redirect"])
35 )
36 )]
37 #[cfg_attr(
38 not(feature = "http2"),
39 arg(
40 long,
41 short = 'f',
42 env = "SERVER_LISTEN_FD",
43 conflicts_with_all(&["host", "port"])
44 )
45 )]
46 pub fd: Option<usize>,
54
55 #[cfg_attr(
56 not(target_family = "wasm"),
57 arg(
58 long,
59 short = 'n',
60 default_value = "1",
61 env = "SERVER_THREADS_MULTIPLIER"
62 )
63 )]
64 #[cfg_attr(
65 target_family = "wasm",
66 arg(
67 long,
68 short = 'n',
69 default_value = "2",
70 env = "SERVER_THREADS_MULTIPLIER"
71 )
72 )] pub threads_multiplier: usize,
78
79 #[cfg_attr(
80 not(target_family = "wasm"),
81 arg(
82 long,
83 short = 'b',
84 default_value = "512",
85 env = "SERVER_MAX_BLOCKING_THREADS"
86 )
87 )]
88 #[cfg_attr(
89 target_family = "wasm",
90 arg(
91 long,
92 short = 'b',
93 default_value = "20",
94 env = "SERVER_MAX_BLOCKING_THREADS"
95 )
96 )] pub max_blocking_threads: usize,
99
100 #[arg(long, short = 'd', default_value = "./public", env = "SERVER_ROOT")]
101 pub root: PathBuf,
103
104 #[arg(long, default_value = "./50x.html", env = "SERVER_ERROR_PAGE_50X")]
105 pub page50x: PathBuf,
109
110 #[arg(long, default_value = "./404.html", env = "SERVER_ERROR_PAGE_404")]
111 pub page404: PathBuf,
115
116 #[cfg(feature = "fallback-page")]
117 #[cfg_attr(docsrs, doc(cfg(feature = "fallback-page")))]
118 #[arg(long, default_value = "", value_parser = value_parser_pathbuf, env = "SERVER_FALLBACK_PAGE")]
119 pub page_fallback: PathBuf,
121
122 #[arg(long, short = 'g', default_value = "error", env = "SERVER_LOG_LEVEL")]
123 pub log_level: String,
125
126 #[arg(
127 long,
128 short = 'c',
129 default_value = "",
130 env = "SERVER_CORS_ALLOW_ORIGINS"
131 )]
132 pub cors_allow_origins: String,
134
135 #[arg(
136 long,
137 short = 'j',
138 default_value = "origin, content-type, authorization",
139 env = "SERVER_CORS_ALLOW_HEADERS"
140 )]
141 pub cors_allow_headers: String,
143
144 #[arg(
145 long,
146 default_value = "origin, content-type",
147 env = "SERVER_CORS_EXPOSE_HEADERS"
148 )]
149 pub cors_expose_headers: String,
151
152 #[arg(
153 long,
154 short = 't',
155 default_value = "false",
156 default_missing_value("true"),
157 num_args(0..=1),
158 require_equals(false),
159 action = clap::ArgAction::Set,
160 env = "SERVER_HTTP2_TLS",
161 )]
162 #[cfg(feature = "http2")]
163 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
164 pub http2: bool,
166
167 #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")]
168 #[cfg(feature = "http2")]
169 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
170 pub http2_tls_cert: Option<PathBuf>,
172
173 #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")]
174 #[cfg(feature = "http2")]
175 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
176 pub http2_tls_key: Option<PathBuf>,
178
179 #[arg(
180 long,
181 default_value = "false",
182 default_missing_value("true"),
183 num_args(0..=1),
184 require_equals(false),
185 action = clap::ArgAction::Set,
186 requires_if("true", "http2"),
187 env = "SERVER_HTTPS_REDIRECT"
188 )]
189 #[cfg(feature = "http2")]
190 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
191 pub https_redirect: bool,
193
194 #[arg(
195 long,
196 requires_if("true", "https_redirect"),
197 default_value = "localhost",
198 env = "SERVER_HTTPS_REDIRECT_HOST"
199 )]
200 #[cfg(feature = "http2")]
201 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
202 pub https_redirect_host: String,
204
205 #[arg(
206 long,
207 requires_if("true", "https_redirect"),
208 default_value = "80",
209 env = "SERVER_HTTPS_REDIRECT_FROM_PORT"
210 )]
211 #[cfg(feature = "http2")]
212 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
213 pub https_redirect_from_port: u16,
215
216 #[arg(
217 long,
218 requires_if("true", "https_redirect"),
219 default_value = "localhost",
220 env = "SERVER_HTTPS_REDIRECT_FROM_HOSTS"
221 )]
222 #[cfg(feature = "http2")]
223 #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
224 pub https_redirect_from_hosts: String,
226
227 #[arg(long, default_value = "index.html", env = "SERVER_INDEX_FILES")]
228 pub index_files: String,
231
232 #[cfg(any(
233 feature = "compression",
234 feature = "compression-gzip",
235 feature = "compression-brotli",
236 feature = "compression-zstd",
237 feature = "compression-deflate"
238 ))]
239 #[cfg_attr(
240 docsrs,
241 doc(cfg(any(
242 feature = "compression",
243 feature = "compression-gzip",
244 feature = "compression-brotli",
245 feature = "compression-zstd",
246 feature = "compression-deflate"
247 )))
248 )]
249 #[arg(
250 long,
251 short = 'x',
252 default_value = "true",
253 default_missing_value("true"),
254 num_args(0..=1),
255 require_equals(false),
256 action = clap::ArgAction::Set,
257 env = "SERVER_COMPRESSION",
258 )]
259 pub compression: bool,
261
262 #[cfg(any(
263 feature = "compression",
264 feature = "compression-gzip",
265 feature = "compression-brotli",
266 feature = "compression-zstd",
267 feature = "compression-deflate"
268 ))]
269 #[cfg_attr(
270 docsrs,
271 doc(cfg(any(
272 feature = "compression",
273 feature = "compression-gzip",
274 feature = "compression-brotli",
275 feature = "compression-zstd",
276 feature = "compression-deflate"
277 )))
278 )]
279 #[arg(long, default_value = "default", env = "SERVER_COMPRESSION_LEVEL")]
280 pub compression_level: super::CompressionLevel,
282
283 #[cfg(any(
284 feature = "compression",
285 feature = "compression-gzip",
286 feature = "compression-brotli",
287 feature = "compression-zstd",
288 feature = "compression-deflate"
289 ))]
290 #[cfg_attr(
291 docsrs,
292 doc(cfg(any(
293 feature = "compression",
294 feature = "compression-gzip",
295 feature = "compression-brotli",
296 feature = "compression-zstd",
297 feature = "compression-deflate"
298 )))
299 )]
300 #[arg(
301 long,
302 default_value = "false",
303 default_missing_value("true"),
304 num_args(0..=1),
305 require_equals(false),
306 action = clap::ArgAction::Set,
307 env = "SERVER_COMPRESSION_STATIC",
308 )]
309 pub compression_static: bool,
312
313 #[cfg(feature = "directory-listing")]
314 #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))]
315 #[arg(
316 long,
317 short = 'z',
318 default_value = "false",
319 default_missing_value("true"),
320 num_args(0..=1),
321 require_equals(false),
322 action = clap::ArgAction::Set,
323 env = "SERVER_DIRECTORY_LISTING",
324 )]
325 pub directory_listing: bool,
327
328 #[cfg(feature = "directory-listing")]
329 #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))]
330 #[arg(
331 long,
332 requires_if("true", "directory_listing"),
333 default_value = "6",
334 env = "SERVER_DIRECTORY_LISTING_ORDER"
335 )]
336 pub directory_listing_order: u8,
338
339 #[cfg(feature = "directory-listing")]
340 #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))]
341 #[arg(
342 long,
343 value_enum,
344 requires_if("true", "directory_listing"),
345 default_value = "html",
346 env = "SERVER_DIRECTORY_LISTING_FORMAT",
347 ignore_case(true)
348 )]
349 pub directory_listing_format: DirListFmt,
351
352 #[arg(
353 long,
354 default_value = "false",
355 default_value_if("http2", "true", Some("true")),
356 default_missing_value("true"),
357 num_args(0..=1),
358 require_equals(false),
359 action = clap::ArgAction::Set,
360 env = "SERVER_SECURITY_HEADERS",
361 )]
362 pub security_headers: bool,
366
367 #[arg(
368 long,
369 short = 'e',
370 default_value = "true",
371 env = "SERVER_CACHE_CONTROL_HEADERS"
372 )]
373 #[arg(
374 long,
375 short = 'e',
376 default_value = "true",
377 default_missing_value("true"),
378 num_args(0..=1),
379 require_equals(false),
380 action = clap::ArgAction::Set,
381 env = "SERVER_CACHE_CONTROL_HEADERS",
382 )]
383 pub cache_control_headers: bool,
385
386 #[cfg(feature = "basic-auth")]
387 #[arg(long, default_value = "", env = "SERVER_BASIC_AUTH")]
389 pub basic_auth: String,
390
391 #[arg(long, short = 'q', default_value = "0", env = "SERVER_GRACE_PERIOD")]
392 pub grace_period: u8,
394
395 #[arg(
396 long,
397 short = 'w',
398 default_value = "./config.toml",
399 value_parser = value_parser_pathbuf,
400 env = "SERVER_CONFIG_FILE"
401 )]
402 pub config_file: PathBuf,
404
405 #[arg(
406 long,
407 default_value = "false",
408 default_missing_value("true"),
409 num_args(0..=1),
410 require_equals(false),
411 action = clap::ArgAction::Set,
412 env = "SERVER_LOG_REMOTE_ADDRESS",
413 )]
414 pub log_remote_address: bool,
416
417 #[arg(
418 long,
419 default_value = "false",
420 default_missing_value("true"),
421 num_args(0..=1),
422 require_equals(false),
423 action = clap::ArgAction::Set,
424 env = "SERVER_LOG_X_REAL_IP",
425 )]
426 pub log_x_real_ip: bool,
428
429 #[arg(
430 long,
431 default_value = "false",
432 default_missing_value("true"),
433 num_args(0..=1),
434 require_equals(false),
435 action = clap::ArgAction::Set,
436 env = "SERVER_LOG_FORWARDED_FOR",
437 )]
438 pub log_forwarded_for: bool,
440
441 #[arg(
442 long,
443 require_equals(false),
444 value_delimiter(','),
445 action = clap::ArgAction::Set,
446 env = "SERVER_TRUSTED_PROXIES",
447 )]
448 pub trusted_proxies: Vec<IpAddr>,
450
451 #[arg(
452 long,
453 default_value = "true",
454 default_missing_value("true"),
455 num_args(0..=1),
456 require_equals(false),
457 action = clap::ArgAction::Set,
458 env = "SERVER_REDIRECT_TRAILING_SLASH",
459 )]
460 pub redirect_trailing_slash: bool,
462
463 #[arg(
464 long,
465 default_value = "false",
466 default_missing_value("true"),
467 num_args(0..=1),
468 require_equals(false),
469 action = clap::ArgAction::Set,
470 env = "SERVER_IGNORE_HIDDEN_FILES",
471 )]
472 pub ignore_hidden_files: bool,
474
475 #[arg(
476 long,
477 default_value = "false",
478 default_missing_value("true"),
479 num_args(0..=1),
480 require_equals(false),
481 action = clap::ArgAction::Set,
482 env = "SERVER_DISABLE_SYMLINKS",
483 )]
484 pub disable_symlinks: bool,
486
487 #[arg(
488 long,
489 default_value = "false",
490 default_missing_value("true"),
491 num_args(0..=1),
492 require_equals(false),
493 action = clap::ArgAction::Set,
494 env = "SERVER_HEALTH",
495 )]
496 pub health: bool,
499
500 #[cfg(all(unix, feature = "experimental"))]
501 #[arg(
502 long = "experimental-metrics",
503 default_value = "false",
504 default_missing_value("true"),
505 num_args(0..=1),
506 require_equals(false),
507 action = clap::ArgAction::Set,
508 env = "SERVER_EXPERIMENTAL_METRICS",
509 )]
510 pub experimental_metrics: bool,
512
513 #[arg(
514 long,
515 default_value = "false",
516 default_missing_value("true"),
517 num_args(0..=1),
518 require_equals(false),
519 action = clap::ArgAction::Set,
520 env = "SERVER_MAINTENANCE_MODE"
521 )]
522 pub maintenance_mode: bool,
524
525 #[arg(
526 long,
527 default_value = "503",
528 value_parser = value_parser_status_code,
529 requires_if("true", "maintenance_mode"),
530 env = "SERVER_MAINTENANCE_MODE_STATUS"
531 )]
532 pub maintenance_mode_status: StatusCode,
534
535 #[arg(
536 long,
537 default_value = "",
538 value_parser = value_parser_pathbuf,
539 requires_if("true", "maintenance_mode"),
540 env = "SERVER_MAINTENANCE_MODE_FILE"
541 )]
542 pub maintenance_mode_file: PathBuf,
544
545 #[cfg(windows)]
549 #[arg(
550 long,
551 short = 's',
552 default_value = "false",
553 default_missing_value("true"),
554 num_args(0..=1),
555 require_equals(false),
556 action = clap::ArgAction::Set,
557 env = "SERVER_WINDOWS_SERVICE",
558 )]
559 pub windows_service: bool,
561
562 #[command(subcommand)]
564 pub commands: Option<Commands>,
566
567 #[arg(
568 long,
569 short = 'V',
570 default_value = "false",
571 default_missing_value("true")
572 )]
573 #[doc(hidden)]
574 pub version: bool,
576}
577
578#[derive(Debug, clap::Subcommand)]
579pub enum Commands {
581 #[cfg(windows)]
583 #[command(name = "install")]
584 Install {},
585
586 #[cfg(windows)]
588 #[command(name = "uninstall")]
589 Uninstall {},
590
591 #[command(name = "generate")]
593 Generate {
594 #[arg(long)]
596 completions: bool,
597 #[arg(long)]
599 man_pages: bool,
600 out_dir: PathBuf,
602 },
603}
604
605fn value_parser_pathbuf(s: &str) -> Result<PathBuf, String> {
606 Ok(PathBuf::from(s))
607}
608
609fn value_parser_status_code(s: &str) -> Result<StatusCode, String> {
610 match s.parse::<u16>() {
611 Ok(code) => StatusCode::from_u16(code).map_err(|err| err.to_string()),
612 Err(err) => Err(err.to_string()),
613 }
614}