Repository: static-web-server/static-web-server (language: rust)

Issue:
Boolean options without a value (--http2) are silently ignored
**Describe the bug**

Boolean options like `--http2` and `--security-headers` only work if you add `=true` to the end, which is kinda non-standard. Seems the blame lies with [clap's peculiar decision](https://github.com/clap-rs/clap/issues/1649) to ignore decades of option-parsing precedence by default. But also it seems that clap v2.0 (which comes from structopt) does not require the user to supply the `=true` and instead will just silently ignore the flag.

**To Reproduce**

```
$ static-web-server --port 8000 --log-level=trace --security-headers --root=./ 2>&1 | grep "security headers"
2023-05-29T16:19:43.950269Z  INFO static_web_server::server: security headers: enabled=false

$ static-web-server --port 8000 --log-level=trace --security-headers=true --root=./ 2>&1 | grep "security headers"
2023-05-29T16:21:45.744096Z  INFO static_web_server::server: security headers: enabled=true

$ static-web-server --port 8000 --log-level=trace --http2 --root=./ 2>&1 | grep "security headers"
2023-05-29T16:21:04.107193Z  INFO static_web_server::server: security headers: enabled=false
(also should have crashed since no tls cert flags were provided)
```

**Expected behavior**
* `--http2` should imply "true", or `--http2` should throw an error that a value is required.

**Environment and Specs**
 - static-web-server: 2.16.0 (built from commit 0f66443)
 - OS: Linux

**Additional context**
If we migrate from structopt to clapv3 (which includes structopt), then it will log `error: The argument '--http2 <HTTP2>' requires a value but none was supplied`. But I'm struggling to implement the "--http2 implies true, no value required" behavior.

I have started down this path of migrating to clapv3 here: https://github.com/static-web-server/static-web-server/compare/master...mac-chaffee:static-web-server:clap-migration



Discussion / hints:
Yeah, this is a dependency issue regarding ignoring boolean options behavior without an explicit value.
But I can say that this is also something not supported on `structopt` as far as I remember in https://github.com/clap-rs/clap/issues/1649 (as you referred).

Anyway, I realized that at this point, [`structopt` is sort of deprecated](https://github.com/TeXitoi/structopt/issues/525) since it is included in `clap` v3+. So a migration path should be followed.
> If we migrate from structopt to clapv3 (which includes structopt), then it will log `error: The argument '--http2 <HTTP2>' requires a value but none was supplied`. But I'm struggling to implement the "--http2 implies true, no value required" behavior.
> 
> I have started down this path of migrating to clapv3 here: [master...mac-chaffee:static-web-server:clap-migration](https://github.com/static-web-server/static-web-server/compare/master...mac-chaffee:static-web-server:clap-migration)

Do you have a plan to submit it as a PR or draft? I think should be great, then I could try to help.
Released on [v2.17.0](https://github.com/static-web-server/static-web-server/releases/tag/v2.17.0)

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index e2c3d05c..0fd3507f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -56,6 +56,55 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "anstream"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is-terminal",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+dependencies = [
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "anyhow"
 version = "1.0.71"
@@ -78,17 +127,6 @@ dependencies = [
  "zstd-safe",
 ]
 
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -233,42 +271,51 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "3.2.25"
+version = "4.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
+checksum = "b4ed2379f8603fa2b7509891660e802b88c70a79a6427a70abb5968054de2c28"
 dependencies = [
- "atty",
- "bitflags",
+ "clap_builder",
  "clap_derive",
- "clap_lex",
- "indexmap",
  "once_cell",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "bitflags",
+ "clap_lex",
  "strsim",
- "termcolor",
- "textwrap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "3.2.25"
+version = "4.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008"
+checksum = "59e9ef9a08ee1c0e1f2e162121665ac45ac3783b0f897db7244ae75ad9a8f65b"
 dependencies = [
  "heck",
- "proc-macro-error",
  "proc-macro2",
  "quote",
- "syn 1.0.109",
+ "syn",
 ]
 
 [[package]]
 name = "clap_lex"
-version = "0.2.4"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
-dependencies = [
- "os_str_bytes",
-]
+checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 
 [[package]]
 name = "core-foundation-sys"
@@ -320,6 +367,27 @@ version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 
+[[package]]
+name = "errno"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "errno-dragonfly"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+dependencies = [
+ "cc",
+ "libc",
+]
+
 [[package]]
 name = "flate2"
 version = "1.0.26"
@@ -479,21 +547,18 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.19"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "hermit-abi"
-version = "0.2.6"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
 
 [[package]]
 name = "http"
@@ -614,6 +679,29 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "io-lifetimes"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "is-terminal"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "itertools"
 version = "0.9.0"
@@ -665,6 +753,12 @@ version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
+
 [[package]]
 name = "listenfd"
 version = "1.0.1"
@@ -769,12 +863,6 @@ version = "1.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
 
-[[package]]
-name = "os_str_bytes"
-version = "6.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
-
 [[package]]
 name = "overload"
 version = "0.1.1"
@@ -827,7 +915,7 @@ checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -848,30 +936,6 @@ version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
-[[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
 [[package]]
 name = "proc-macro2"
 version = "1.0.59"
@@ -931,6 +995,20 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "rustix"
+version = "0.37.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "rustls"
 version = "0.21.1"
@@ -1001,7 +1079,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -1032,7 +1110,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -1170,17 +1248,6 @@ version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
 
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
 [[package]]
 name = "syn"
 version = "2.0.18"
@@ -1192,21 +1259,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "termcolor"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
-
 [[package]]
 name = "thread_local"
 version = "1.1.7"
@@ -1263,7 +1315,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
 ]
 
 [[package]]
@@ -1385,6 +1437,12 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
 [[package]]
 name = "uuid"
 version = "1.3.3"
@@ -1440,7 +1498,7 @@ dependencies = [
  "once_cell",
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
  "wasm-bindgen-shared",
 ]
 
@@ -1462,7 +1520,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.18",
+ "syn",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
@@ -1505,15 +1563,6 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
-[[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi",
-]
-
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
diff --git a/Cargo.toml b/Cargo.toml
index bc9e4c5d..da62d8f7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -70,7 +70,7 @@ rustls-pemfile = { version = "1.0", optional = true }
 serde = { version = "1.0", default-features = false, features = ["derive"] }
 serde_ignored = "0.1"
 serde_repr = "0.1"
-clap = { version = "3.0", features = ["derive", "env"] }
+clap = { version = "4.3", features = ["derive", "env"] }
 chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }
 tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"] }
 tokio-rustls = { version = "0.24", optional = true }
diff --git a/src/directory_listing.rs b/src/directory_listing.rs
index 952db874..fb13ed5d 100644
--- a/src/directory_listing.rs
+++ b/src/directory_listing.rs
@@ -9,7 +9,7 @@
 #![allow(missing_docs)]
 
 use chrono::{DateTime, Local, NaiveDateTime, Utc};
-use clap::arg_enum;
+use clap::ValueEnum;
 use futures_util::future::Either;
 use futures_util::{future, FutureExt};
 use headers::{ContentLength, ContentType, HeaderMapExt};
@@ -25,14 +25,12 @@ use std::time::{SystemTime, UNIX_EPOCH};
 
 use crate::{exts::http::MethodExt, Context, Result};
 
-arg_enum! {
-    #[derive(Debug, Serialize, Deserialize, Clone)]
-    #[serde(rename_all = "lowercase")]
-    /// Directory listing output format for file entries.
-    pub enum DirListFmt {
-        Html,
-        Json,
-    }
+#[derive(Debug, Serialize, Deserialize, Clone, ValueEnum)]
+#[serde(rename_all = "lowercase")]
+/// Directory listing output format for file entries.
+pub enum DirListFmt {
+    Html,
+    Json,
 }
 
 /// Provides directory listing support for the current request.
diff --git a/src/server.rs b/src/server.rs
index 93630b2c..0fbbfe16 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -196,7 +196,7 @@ impl Server {
 
         // Directory listing format
         let dir_listing_format = general.directory_listing_format;
-        tracing::info!("directory listing format: {}", dir_listing_format);
+        tracing::info!("directory listing format: {:?}", dir_listing_format);
 
         // Cache control headers option
         let cache_control_headers = general.cache_control_headers;
diff --git a/src/settings/cli.rs b/src/settings/cli.rs
index aa2b8969..d6e34fc8 100644
--- a/src/settings/cli.rs
+++ b/src/settings/cli.rs
@@ -5,28 +5,28 @@
 
 //! The server CLI options
 
-use clap::StructOpt;
+use clap::Parser;
 use std::path::PathBuf;
 
 use crate::directory_listing::DirListFmt;
 
 /// General server configuration available in CLI and config file options.
-#[derive(Debug, StructOpt)]
-#[structopt(about, author, version)]
+#[derive(Parser, Debug)]
+#[command(author, version, about, long_about = None)]
 pub struct General {
-    #[structopt(long, short = 'a', default_value = "::", env = "SERVER_HOST")]
+    #[arg(long, short = 'a', default_value = "::", env = "SERVER_HOST")]
     /// Host address (E.g 127.0.0.1 or ::1)
     pub host: String,
 
-    #[structopt(long, short = 'p', default_value = "80", env = "SERVER_PORT")]
+    #[arg(long, short = 'p', default_value = "80", env = "SERVER_PORT")]
     /// Host port
     pub port: u16,
 
-    #[structopt(
+    #[arg(
         long,
         short = 'f',
         env = "SERVER_LISTEN_FD",
-        conflicts_with_all(&["host", "port", "https-redirect"])
+        conflicts_with_all(&["host", "port", "https_redirect"])
     )]
     /// Instead of binding to a TCP port, accept incoming connections to an already-bound TCP
     /// socket listener on the specified file descriptor number (usually zero). Requires that the
@@ -39,7 +39,7 @@ pub struct General {
 
     #[cfg_attr(
         not(wasm),
-        structopt(
+        arg(
             long,
             short = 'n',
             default_value = "1",
@@ -48,7 +48,7 @@ pub struct General {
     )]
     #[cfg_attr(
         wasm,
-        structopt(
+        arg(
             long,
             short = 'n',
             default_value = "2",
@@ -63,7 +63,7 @@ pub struct General {
 
     #[cfg_attr(
         not(wasm),
-        structopt(
+        arg(
             long,
             short = 'b',
             default_value = "512",
@@ -72,7 +72,7 @@ pub struct General {
     )]
     #[cfg_attr(
         wasm,
-        structopt(
+        arg(
             long,
             short = 'b',
             default_value = "20",
@@ -82,11 +82,11 @@ pub struct General {
     /// Maximum number of blocking threads
     pub max_blocking_threads: usize,
 
-    #[structopt(long, short = 'd', default_value = "./public", env = "SERVER_ROOT")]
+    #[arg(long, short = 'd', default_value = "./public", env = "SERVER_ROOT")]
     /// Root directory path of static files.
     pub root: PathBuf,
 
-    #[structopt(
+    #[arg(
         long,
         default_value = "./public/50x.html",
         env = "SERVER_ERROR_PAGE_50X"
@@ -94,7 +94,7 @@ pub struct General {
     /// HTML file path for 50x errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message.
     pub page50x: PathBuf,
 
-    #[structopt(
+    #[arg(
         long,
         default_value = "./public/404.html",
         env = "SERVER_ERROR_PAGE_404"
@@ -102,15 +102,15 @@ pub struct General {
     /// HTML file path for 404 errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message.
     pub page404: PathBuf,
 
-    #[structopt(long, env = "SERVER_FALLBACK_PAGE")]
+    #[arg(long, env = "SERVER_FALLBACK_PAGE")]
     /// HTML file path that is used for GET requests when the requested path doesn't exist. The fallback page is served with a 200 status code, useful when using client routers. If the path is not specified or simply doesn't exist then this feature will not be active.
     pub page_fallback: Option<PathBuf>,
 
-    #[structopt(long, short = 'g', default_value = "error", env = "SERVER_LOG_LEVEL")]
+    #[arg(long, short = 'g', default_value = "error", env = "SERVER_LOG_LEVEL")]
     /// Specify a logging level in lower case. Values: error, warn, info, debug or trace
     pub log_level: String,
 
-    #[structopt(
+    #[arg(
         long,
         short = 'c',
         default_value = "",
@@ -119,7 +119,7 @@ pub struct General {
     /// Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host.
     pub cors_allow_origins: String,
 
-    #[structopt(
+    #[arg(
         long,
         short = 'j',
         default_value = "origin, content-type",
@@ -128,7 +128,7 @@ pub struct General {
     /// Specify an optional CORS list of allowed headers separated by commas. Default "origin, content-type". It requires `--cors-allow-origins` to be used along with.
     pub cors_allow_headers: String,
 
-    #[structopt(
+    #[arg(
         long,
         default_value = "origin, content-type",
         env = "SERVER_CORS_EXPOSE_HEADERS"
@@ -136,35 +136,41 @@ pub struct General {
     /// Specify an optional CORS list of exposed headers separated by commas. Default "origin, content-type". It requires `--cors-expose-origins` to be used along with.
     pub cors_expose_headers: String,
 
-    #[structopt(
+    #[arg(
         long,
         short = 't',
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_HTTP2_TLS"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_HTTP2_TLS",
     )]
     #[cfg(feature = "http2")]
     #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
     /// Enable HTTP/2 with TLS support.
     pub http2: bool,
 
-    #[structopt(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")]
+    #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_CERT")]
     #[cfg(feature = "http2")]
     #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
     /// Specify the file path to read the certificate.
     pub http2_tls_cert: Option<PathBuf>,
 
-    #[structopt(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")]
+    #[arg(long, required_if_eq("http2", "true"), env = "SERVER_HTTP2_TLS_KEY")]
     #[cfg(feature = "http2")]
     #[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
     /// Specify the file path to read the private key.
     pub http2_tls_key: Option<PathBuf>,
 
-    #[structopt(
+    #[arg(
         long,
-        requires_if("true", "http2"),
-        parse(try_from_str),
         default_value = "false",
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        requires_if("true", "http2"),
         env = "SERVER_HTTPS_REDIRECT"
     )]
     #[cfg(feature = "http2")]
@@ -172,9 +178,9 @@ pub struct General {
     /// Redirect all requests with scheme "http" to "https" for the current server instance. It depends on "http2" to be enabled.
     pub https_redirect: bool,
 
-    #[structopt(
+    #[arg(
         long,
-        requires_if("true", "https-redirect"),
+        requires_if("true", "https_redirect"),
         default_value = "localhost",
         env = "SERVER_HTTPS_REDIRECT_HOST"
     )]
@@ -183,9 +189,9 @@ pub struct General {
     /// Canonical host name or IP of the HTTPS (HTTPS/2) server. It depends on "https_redirect" to be enabled.
     pub https_redirect_host: String,
 
-    #[structopt(
+    #[arg(
         long,
-        requires_if("true", "https-redirect"),
+        requires_if("true", "https_redirect"),
         default_value = "80",
         env = "SERVER_HTTPS_REDIRECT_FROM_PORT"
     )]
@@ -194,9 +200,9 @@ pub struct General {
     /// HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on "https_redirect" to be enabled.
     pub https_redirect_from_port: u16,
 
-    #[structopt(
+    #[arg(
         long,
-        requires_if("true", "https-redirect"),
+        requires_if("true", "https_redirect"),
         default_value = "localhost",
         env = "SERVER_HTTPS_REDIRECT_FROM_HOSTS"
     )]
@@ -207,114 +213,145 @@ pub struct General {
 
     #[cfg(feature = "compression")]
     #[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
-    #[structopt(
+    #[arg(
         long,
         short = 'x',
-        parse(try_from_str),
         default_value = "true",
-        env = "SERVER_COMPRESSION"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_COMPRESSION",
     )]
     /// Gzip, Deflate, Brotli or Zstd compression on demand determined by the Accept-Encoding header and applied to text-based web file types only.
     pub compression: bool,
 
     #[cfg(feature = "compression")]
     #[cfg_attr(docsrs, doc(cfg(feature = "compression")))]
-    #[structopt(
+    #[arg(
         long,
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_COMPRESSION_STATIC"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_COMPRESSION_STATIC",
     )]
     /// Look up the pre-compressed file variant (`.gz`, `.br` or `.zst`) on disk of a requested file and serves it directly if available.
     /// The compression type is determined by the `Accept-Encoding` header.
     pub compression_static: bool,
 
-    #[structopt(
+    #[arg(
         long,
         short = 'z',
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_DIRECTORY_LISTING"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_DIRECTORY_LISTING",
     )]
     /// Enable directory listing for all requests ending with the slash character (‘/’).
     pub directory_listing: bool,
 
-    #[structopt(
+    #[arg(
         long,
-        required_if_eq("directory-listing", "true"),
+        requires_if("true", "directory_listing"),
         default_value = "6",
         env = "SERVER_DIRECTORY_LISTING_ORDER"
     )]
     /// Specify a default code number to order directory listing entries per `Name`, `Last modified` or `Size` attributes (columns). Code numbers supported: 0 (Name asc), 1 (Name desc), 2 (Last modified asc), 3 (Last modified desc), 4 (Size asc), 5 (Size desc). Default 6 (unordered)
     pub directory_listing_order: u8,
 
-    #[structopt(
+    #[arg(
         long,
-        required_if_eq("directory-listing", "true"),
+        value_enum,
+        requires_if("true", "directory_listing"),
         default_value = "html",
         env = "SERVER_DIRECTORY_LISTING_FORMAT",
-        case_insensitive = true
+        ignore_case(true)
     )]
     /// Specify a content format for directory listing entries. Formats supported: "html" or "json". Default "html".
     pub directory_listing_format: DirListFmt,
 
-    #[structopt(
+    #[arg(
         long,
-        parse(try_from_str),
-        default_value_if("http2", Some("true"), Some("true")),
         default_value = "false",
-        env = "SERVER_SECURITY_HEADERS"
+        default_value_if("http2", "true", Some("true")),
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_SECURITY_HEADERS",
     )]
     /// Enable security headers by default when HTTP/2 feature is activated.
     /// Headers included: "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload" (2 years max-age),
     /// "X-Frame-Options: DENY", "X-XSS-Protection: 1; mode=block" and "Content-Security-Policy: frame-ancestors 'self'".
     pub security_headers: bool,
 
-    #[structopt(
+    #[arg(
         long,
         short = 'e',
-        parse(try_from_str),
         default_value = "true",
         env = "SERVER_CACHE_CONTROL_HEADERS"
     )]
+    #[arg(
+        long,
+        short = 'e',
+        default_value = "true",
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_CACHE_CONTROL_HEADERS",
+    )]
     /// Enable cache control headers for incoming requests based on a set of file types. The file type list can be found on `src/control_headers.rs` file.
     pub cache_control_headers: bool,
 
     /// It provides The "Basic" HTTP Authentication scheme using credentials as "user-id:password" pairs. Password must be encoded using the "BCrypt" password-hashing function.
-    #[structopt(long, default_value = "", env = "SERVER_BASIC_AUTH")]
+    #[arg(long, default_value = "", env = "SERVER_BASIC_AUTH")]
     pub basic_auth: String,
 
-    #[structopt(long, short = 'q', default_value = "0", env = "SERVER_GRACE_PERIOD")]
+    #[arg(long, short = 'q', default_value = "0", env = "SERVER_GRACE_PERIOD")]
     /// Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds.
     pub grace_period: u8,
 
-    #[structopt(long, short = 'w', env = "SERVER_CONFIG_FILE")]
+    #[arg(long, short = 'w', env = "SERVER_CONFIG_FILE")]
     /// Server TOML configuration file path.
     pub config_file: Option<PathBuf>,
 
-    #[structopt(
+    #[arg(
         long,
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_LOG_REMOTE_ADDRESS"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_LOG_REMOTE_ADDRESS",
     )]
     /// Log incoming requests information along with its remote address if available using the `info` log level.
     pub log_remote_address: bool,
 
-    #[structopt(
+    #[arg(
         long,
-        parse(try_from_str),
         default_value = "true",
-        env = "SERVER_REDIRECT_TRAILING_SLASH"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_REDIRECT_TRAILING_SLASH",
     )]
     /// Check for a trailing slash in the requested directory URI and redirect permanently (308) to the same path with a trailing slash suffix if it is missing.
     pub redirect_trailing_slash: bool,
 
-    #[structopt(
+    #[arg(
         long,
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_IGNORE_HIDDEN_FILES"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_IGNORE_HIDDEN_FILES",
     )]
     /// Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing).
     pub ignore_hidden_files: bool,
@@ -323,32 +360,35 @@ pub struct General {
     // Windows specific arguments and commands
     //
     #[cfg(windows)]
-    #[structopt(
+    #[arg(
         long,
         short = 's',
-        parse(try_from_str),
         default_value = "false",
-        env = "SERVER_WINDOWS_SERVICE"
+        default_missing_value("true"),
+        num_args(0..=1),
+        require_equals(true),
+        action = clap::ArgAction::Set,
+        env = "SERVER_WINDOWS_SERVICE",
     )]
     /// Tell the web server to run in a Windows Service context. Note that the `install` subcommand will enable this option automatically.
     pub windows_service: bool,
 
     // Windows commands
     #[cfg(windows)]
-    #[structopt(subcommand)]
+    #[command(subcommand)]
     /// Subcommands to install or uninstall the SWS Windows Service.
     pub commands: Option<Commands>,
 }
 
 #[cfg(windows)]
-#[derive(Debug, StructOpt)]
+#[derive(Debug, clap::Subcommand)]
 /// Subcommands to install or uninstall the SWS Windows Service.
 pub enum Commands {
     /// Install a Windows Service for the web server.
-    #[structopt(name = "install")]
+    #[command(name = "install")]
     Install {},
 
     /// Uninstall the current Windows Service.
-    #[structopt(name = "uninstall")]
+    #[command(name = "uninstall")]
     Uninstall {},
 }
diff --git a/src/settings/mod.rs b/src/settings/mod.rs
index bf94bd8f..746d9d7a 100644
--- a/src/settings/mod.rs
+++ b/src/settings/mod.rs
@@ -6,7 +6,7 @@
 //! Module that provides all settings of SWS.
 //!
 
-use clap::StructOpt;
+use clap::Parser;
 use globset::{Glob, GlobMatcher};
 use headers::HeaderMap;
 use hyper::StatusCode;
@@ -68,7 +68,7 @@ pub struct Settings {
 impl Settings {
     /// Handles CLI and config file options and converging them into one.
     pub fn get() -> Result<Settings> {
-        let opts = General::from_args();
+        let opts = General::parse();
 
         // Define the general CLI/file options
         let mut host = opts.host;


Test patch: