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