From 7b578fdeb835f230c3d8effc862ceabfb9f5cead Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Sat, 13 Jul 2024 12:49:21 -0400 Subject: [PATCH] Replace structopt with clap. --- Cargo.lock | 155 ++++++++-------------------------------------------- Cargo.toml | 2 +- src/http.rs | 2 +- src/main.rs | 56 +++++++------------ 4 files changed, 46 insertions(+), 169 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b3cb26f..654871d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -314,15 +314,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "anstream" version = "0.6.14" @@ -389,17 +380,6 @@ dependencies = [ "syn 2.0.71", ] -[[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 0.3.9", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -591,21 +571,6 @@ dependencies = [ "half", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.5.9" @@ -613,6 +578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -621,8 +587,22 @@ version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ + "anstream", "anstyle", "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.71", ] [[package]] @@ -711,7 +691,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.9", + "clap", "criterion-plot", "is-terminal", "itertools", @@ -1099,15 +1079,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.1" @@ -1115,13 +1086,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" @@ -1266,7 +1234,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", "windows-sys", ] @@ -1508,7 +1476,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi", "libc", ] @@ -1538,6 +1506,7 @@ dependencies = [ "actix-web", "anyhow", "bytes 0.5.6", + "clap", "criterion", "env_logger", "futures", @@ -1552,7 +1521,6 @@ dependencies = [ "serde", "serde_json", "stderrlog", - "structopt", "tempfile", "test-log", "thiserror", @@ -1733,30 +1701,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[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-macro-hack" version = "0.5.20+deprecated" @@ -2189,33 +2133,9 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" [[package]] name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -2282,15 +2202,6 @@ dependencies = [ "syn 2.0.71", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.62" @@ -2567,18 +2478,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" - [[package]] name = "url" version = "2.5.2" @@ -2602,12 +2501,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index d17a324..9fe8c20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ actix-rt = "1.1.1" actix-web = "3.3.3" anyhow = "1.0.86" bytes = "0.5.6" +clap = { version = "4.5.9", features = ["derive"] } futures = "0.3.30" handlebars = "5.1.2" log = "0.4.22" @@ -33,7 +34,6 @@ mime_guess = "2.0.5" serde = { version = "1.0.204", features = ["derive"] } serde_json = "1.0.120" stderrlog = "0.6.0" -structopt = "0.3.26" thiserror = "1.0.62" walkdir = "2.5.0" diff --git a/src/http.rs b/src/http.rs index 562f506..b4dc570 100644 --- a/src/http.rs +++ b/src/http.rs @@ -28,7 +28,7 @@ pub struct InvalidQueryStringError { source: QueryPayloadError, } -#[derive(Default)] +#[derive(Clone, Default)] pub struct QueryString(HashMap); impl From for HashMap { diff --git a/src/main.rs b/src/main.rs index 7d714d2..02b6819 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use anyhow::Context; +use clap::{Parser, Subcommand}; use operator::content::{ContentDirectory, MediaRange, Route}; use operator::http::QueryString; use operator::*; @@ -7,72 +8,61 @@ use std::io; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::process; -use structopt::StructOpt; -#[derive(StructOpt)] -#[structopt(about)] +#[derive(Parser)] +#[command(version, about, propagate_version = true)] struct OperatorCommand { /// Silence all output. - #[structopt(long, short = "q", global = true)] + #[arg(short, long, global = true)] quiet: bool, /// Verbose mode; multiple -v options increase the verbosity. - #[structopt(long, short = "v", parse(from_occurrences), global = true)] - verbose: usize, + #[arg(short, long, global = true, action = clap::ArgAction::Count)] + verbose: u8, - #[structopt(subcommand)] + #[command(subcommand)] subcommand: OperatorSubcommand, } -#[derive(StructOpt)] +#[derive(Subcommand)] enum OperatorSubcommand { /// Evaluates a handlebars template from STDIN. - #[structopt(after_help = concat!( - "EXAMPLE:\n", - " echo '{{#if true}}hello world{{/if}}' | operator eval --content-directory=/dev/null" - ), display_order = 0)] Eval { /// Path to a directory containing content files. /// /// Files in this directory can be referenced from the provided /// handlebars template. - #[structopt(long, parse(from_os_str), value_name = "path")] + #[arg(long, value_name = "path")] content_directory: PathBuf, /// Optional query parameters. /// /// This uses the same format as HTTP requests (without a leading "?"). /// For example: --query="a=1&b=2". - #[structopt(long, value_name = "query-string")] + #[arg(long, value_name = "query-string")] query: Option, }, /// Renders content from a content directory. - #[structopt(after_help = concat!( - "EXAMPLE:\n", - " mkdir -p content\n", - " echo 'hello world' > content/hello.txt\n", - " operator get --content-directory=./content --route=/hello" - ), display_order = 1)] Get { /// Path to a directory containing content files. /// /// The route argument refers to files within this directory. - #[structopt(long, parse(from_os_str), value_name = "path")] + #[clap(long, value_name = "path")] content_directory: PathBuf, /// Route specifying which piece of content to get. /// /// Routes are extension-less slash-delimited paths rooted in the /// content directory. They must begin with a slash. - #[structopt(long, value_name = "route")] + #[clap(long, value_name = "route")] route: Route, /// Optional query parameters. /// /// This uses the same format as HTTP requests (without a leading "?"). /// For example: --query="a=1&b=2". - #[structopt(long, value_name = "query-string")] + #[clap(long, value_name = "query-string")] query: Option, /// Declares what types of media are acceptable as output. @@ -80,29 +70,23 @@ enum OperatorSubcommand { /// This serves the same purpose as the HTTP Accept header: to drive /// content negotiation. Unlike the Accept header it is only a single /// media range. Defaults to "*/*". - #[structopt(long, value_name = "media-range")] + #[clap(long, value_name = "media-range")] accept: Option, }, /// Starts an HTTP server. - #[structopt(after_help = concat!( - "EXAMPLE:\n", - " mkdir -p site\n", - " echo 'my websiteunder construction' > site/home.html\n", - " operator -vv serve --bind-to=127.0.0.1:8080 --content-directory=./site --index-route=/home", - ), display_order = 2)] Serve { /// Path to a directory containing content files. /// /// This directory is used to create the website. - #[structopt(long, parse(from_os_str), value_name = "path")] + #[clap(long, value_name = "path")] content_directory: PathBuf, /// What to serve when the request URI has an empty path. /// /// A request for http://mysite.com/ gets a response from this route. /// If this option is not set, such requests always receive a 404. - #[structopt(long, value_name = "route")] + #[clap(long, value_name = "route")] index_route: Option, /// What to serve when there are errors. @@ -113,19 +97,19 @@ enum OperatorSubcommand { /// /// If the error handler itself fails then a default error message is /// used. - #[structopt(long, value_name = "route")] + #[clap(long, value_name = "route")] error_handler_route: Option, /// The TCP address/port that the server should bind to. /// /// This is an IP address and port number. For example, "127.0.0.1:80". - #[structopt(long, value_name = "socket-address")] + #[clap(long, value_name = "socket-address")] bind_to: SocketAddr, }, } fn main() { - let command = OperatorCommand::from_args(); + let command = OperatorCommand::parse(); let stdin = io::stdin(); let stdout = io::stdout(); @@ -134,7 +118,7 @@ fn main() { let result = stderrlog::new() .quiet(command.quiet) - .verbosity(command.verbose) + .verbosity(usize::from(command.verbose)) .timestamp(stderrlog::Timestamp::Millisecond) .init() .map_err(anyhow::Error::from)