Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cli: Update to clap 4 #218

Merged
merged 7 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,078 changes: 628 additions & 450 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ hkdf = "0.12.2"
hex = { version = "0.4.2", features = ["serde"] }
rand = "0.8.0"
log = "0.4.13"
base64 = "0.21.0"
base64 = "0.22.0"
futures_ringbuf = "0.4.0"
time = { version = "0.3.7", features = ["formatting"] }
instant = { version = "0.1.12", features = ["wasm-bindgen"] }
Expand Down Expand Up @@ -63,12 +63,12 @@ tar = { version = "0.4.33", optional = true }
[target.'cfg(not(target_family = "wasm"))'.dependencies]
libc = "0.2.101"
async-std = { version = "1.12.0", features = ["attributes", "unstable"] }
async-tungstenite = { version = "0.25", features = ["async-std-runtime"] }
async-tungstenite = { version = "0.26", features = ["async-std-runtime"] }
async-io = "2.2.0"

# Transit
socket2 = { version = "0.5.0", optional = true, features = ["all"] }
if-addrs = { version = "0.11", optional = true }
if-addrs = { version = "0.13", optional = true }

# Transfer

Expand Down Expand Up @@ -98,7 +98,7 @@ transit = [
transfer = ["transit", "tar", "async-tar", "rmp-serde", "zstd"]
forwarding = ["transit", "rmp-serde"]
default = ["transit", "transfer"]
all = ["default", "forwarding"]
all = ["default", "forwarding", "native-tls"]

# TLS implementations for websocket connections via async-tungstenite
# required for optional wss connection to the mailbox server
Expand Down
7 changes: 5 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ rand = "0.8.3"

# CLI specific dependencies
magic-wormhole = { path = "..", features = ["all"] }
clap = { version = "3.1.5", features = ["cargo", "derive", "wrap_help"] }
clap_complete = "3.1.4"
clap = { version = "4", features = ["cargo", "derive", "help"] }
clap_complete = "4"
env_logger = "0.11"
console = "0.15.0"
indicatif = "0.17.0"
Expand All @@ -28,3 +28,6 @@ qr2term = "0.3.0"
arboard = { version = "3.2.0", features = [
"wayland-data-control",
] } # Wayland by default, fallback to X11.

[dev-dependencies]
trycmd = "0.15"
130 changes: 65 additions & 65 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ fn install_ctrlc_handler(
struct CommonSenderArgs {
/// Suggest a different name to the receiver to keep the file's actual name secret.
/// Not allowed when sending more than one file.
#[clap(long = "rename", visible_alias = "name", value_name = "FILE_NAME")]
#[arg(long = "rename", visible_alias = "name", value_name = "FILE_NAME")]
file_name: Option<String>,
#[clap(
#[arg(
index = 1,
required = true,
min_values = 1,
num_args = 1..,
value_name = "FILENAME|DIRNAME",
value_hint = clap::ValueHint::AnyPath,
)]
Expand All @@ -73,103 +73,97 @@ struct CommonSenderArgs {
#[derive(Debug, Args)]
struct CommonLeaderArgs {
/// Enter a code instead of generating one automatically
#[clap(long, value_name = "CODE")]
#[arg(long, value_name = "CODE")]
code: Option<String>,
/// Length of code (in bytes/words)
#[clap(short = 'c', long, value_name = "NUMWORDS", default_value = "2")]
#[arg(short = 'c', long, value_name = "NUMWORDS", default_value = "2")]
code_length: usize,
}

// receive
#[derive(Debug, Args)]
struct CommonReceiverArgs {
/// Store transferred file or folder in the specified directory. Defaults to $PWD.
#[clap(long = "out-dir", value_name = "PATH", default_value = ".", value_hint = clap::ValueHint::DirPath)]
#[arg(long = "out-dir", value_name = "PATH", default_value = ".", value_hint = clap::ValueHint::DirPath)]
file_path: PathBuf,
}

// receive, connect
#[derive(Debug, Args)]
struct CommonFollowerArgs {
/// Provide the code now rather than typing it interactively
#[clap(value_name = "CODE")]
#[arg(value_name = "CODE")]
code: Option<String>,
}

// send, send-mane, receive, serve, connect
#[derive(Debug, Clone, Args)]
struct CommonArgs {
/// Use a custom relay server (specify multiple times for multiple relays)
#[clap(
long = "relay-server",
#[arg(
long,
visible_alias = "relay",
multiple_occurrences = true,
action = clap::ArgAction::Append,
value_name = "tcp://HOSTNAME:PORT",
value_hint = clap::ValueHint::Url
value_hint = clap::ValueHint::Url,
)]
relay_server: Vec<url::Url>,
/// Use a custom rendezvous server. Both sides need to use the same value in order to find each other.
#[clap(long, value_name = "ws://example.org", value_hint = clap::ValueHint::Url)]
#[arg(long, value_name = "ws://example.org", value_hint = clap::ValueHint::Url)]
rendezvous_server: Option<url::Url>,
/// Disable the relay server support and force a direct connection.
#[clap(long)]
#[arg(long)]
force_direct: bool,
/// Always route traffic over a relay server. This hides your IP address from the peer (but not from the server operators. Use Tor for that).
#[clap(long, conflicts_with = "force-direct")]
#[arg(long, conflicts_with = "force_direct")]
force_relay: bool,
}

#[derive(Debug, Subcommand)]
#[clap(arg_required_else_help = true)]
#[command(arg_required_else_help = true)]
enum ForwardCommand {
/// Make the following ports of your system available to your peer
#[clap(
#[command(
visible_alias = "open",
alias = "server", /* Muscle memory <3 */
mut_arg("help", |a| a.help("Print this help message")),
)]
Serve {
/// List of ports to open up. You can optionally specify a domain/address to forward remote ports
#[clap(value_name = "[DOMAIN:]PORT", multiple_occurrences = true, value_hint = clap::ValueHint::Hostname)]
#[arg(value_name = "[DOMAIN:]PORT", action = clap::ArgAction::Append, value_hint = clap::ValueHint::Hostname)]
targets: Vec<String>,
#[clap(flatten)]
#[command(flatten)]
common: CommonArgs,
#[clap(flatten)]
#[command(flatten)]
common_leader: CommonLeaderArgs,
},
/// Connect to some ports forwarded to you
#[clap(
mut_arg("help", |a| a.help("Print this help message")),
)]
#[command()]
Connect {
/// Bind to specific ports instead of taking random free high ports. Can be provided multiple times.
#[clap(
#[arg(
short = 'p',
long = "port",
multiple_occurrences = true,
action = clap::ArgAction::Append,
value_name = "PORT"
)]
ports: Vec<u16>,
/// Bind to a specific address to accept the forwarding. Depending on your system and firewall, this may make the forwarded ports accessible from the outside.
#[clap(long = "bind", value_name = "ADDRESS", default_value = "::", value_hint = clap::ValueHint::Other)]
#[arg(long = "bind", value_name = "ADDRESS", default_value = "::", value_hint = clap::ValueHint::Other)]
bind_address: std::net::IpAddr,
/// Accept the forwarding without asking for confirmation
#[clap(long, visible_alias = "yes")]
#[arg(long, visible_alias = "yes")]
noconfirm: bool,
#[clap(flatten)]
#[command(flatten)]
common: CommonArgs,
#[clap(flatten)]
#[command(flatten)]
common_follower: CommonFollowerArgs,
},
}

#[derive(Debug, Subcommand)]
enum WormholeCommand {
/// Send a file or a folder
#[clap(
visible_alias = "tx",
mut_arg("help", |a| a.help("Print this help message")),
)]
#[command(visible_alias = "tx")]
Send {
#[clap(flatten)]
common: CommonArgs,
Expand All @@ -178,9 +172,21 @@ enum WormholeCommand {
#[clap(flatten)]
common_send: CommonSenderArgs,
},
/// Send a file to many recipients. READ HELP PAGE FIRST!
#[clap(
mut_arg("help", |a| a.help("Print this help message")),
/// Receive a file or a folder
#[command(visible_alias = "rx")]
Receive {
/// Accept file transfer without asking for confirmation
#[arg(long, visible_alias = "yes")]
noconfirm: bool,
#[command(flatten)]
common: CommonArgs,
#[command(flatten)]
common_follower: CommonFollowerArgs,
#[command(flatten)]
common_receiver: CommonReceiverArgs,
},
/// Send a file to many recipients
#[command(
after_help = "This works by sending the file in a loop with the same code over \
and over again. Note that this also gives an attacker multiple tries \
to guess the code, whereas normally they have only one. This can be \
Expand All @@ -192,49 +198,33 @@ enum WormholeCommand {
SendMany {
/// Only send the file up to n times, limiting the number of people that may receive it.
/// These are also the number of tries a potential attacker gets at guessing the password.
#[clap(short = 'n', long, value_name = "N", default_value = "30")]
#[arg(short = 'n', long, value_name = "N", default_value = "30")]
tries: u64,
/// Automatically stop providing the file after a certain amount of time.
#[clap(long, value_name = "MINUTES", default_value = "60")]
#[arg(long, value_name = "MINUTES", default_value = "60")]
timeout: u64,
#[clap(flatten)]
#[command(flatten)]
common: CommonArgs,
#[clap(flatten)]
#[command(flatten)]
common_leader: CommonLeaderArgs,
#[clap(flatten)]
#[command(flatten)]
common_send: CommonSenderArgs,
},
/// Receive a file or a folder
#[clap(
visible_alias = "rx",
mut_arg("help", |a| a.help("Print this help message")),
)]
Receive {
/// Accept file transfer without asking for confirmation
#[clap(long, visible_alias = "yes")]
noconfirm: bool,
#[clap(flatten)]
common: CommonArgs,
#[clap(flatten)]
common_follower: CommonFollowerArgs,
#[clap(flatten)]
common_receiver: CommonReceiverArgs,
},
/// Forward ports from one machine to another
#[clap(subcommand)]
#[command(subcommand)]
Forward(ForwardCommand),
/// Generate shell completions for the wormhole CLI
#[clap(hide = true)]
#[command(hide = true)]
Completion {
/// The shell type to generate completions for (bash, elvish, powershell, zsh)
shell: clap_complete::Shell,
},
#[clap(hide = true)]
#[command(hide = true)]
Help,
}

#[derive(Debug, Parser)]
#[clap(
#[command(
version,
author,
about,
Expand All @@ -243,12 +233,17 @@ enum WormholeCommand {
propagate_version = true,
after_help = "Run a subcommand with `--help` to know how it's used.\n\
To send files, use `wormhole send <PATH>`.\n\
To receive files, use `wormhole receive <CODE>`.",
mut_arg("help", |a| a.help("Print this help message")),
To receive files, use `wormhole receive <CODE>`."
)]
struct WormholeCli {
/// Enable logging to stdout, for debugging purposes
#[clap(short = 'v', long = "verbose", alias = "log", global = true)]
#[arg(
short = 'v',
long = "verbose",
alias = "log",
global = true,
display_order = 100
)]
log: bool,
#[clap(subcommand)]
command: WormholeCommand,
Expand Down Expand Up @@ -1138,7 +1133,7 @@ mod test {

#[test]
fn test_shell_completion() {
use clap::ArgEnum;
use clap::ValueEnum;

for shell in clap_complete::Shell::value_variants() {
let mut cmd = WormholeCli::command();
Expand All @@ -1149,4 +1144,9 @@ mod test {
String::from_utf8(out).unwrap();
}
}

#[test]
fn verify_cli() {
WormholeCli::command().debug_assert();
}
}
8 changes: 8 additions & 0 deletions cli/tests/cli_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! Integration tests for the wormhole cli

#[test]
fn trycmd() {
trycmd::TestCases::new()
.case("tests/cmd/*.trycmd")
.case("tests/cmd/*.toml");
}
16 changes: 16 additions & 0 deletions cli/tests/cmd/basic.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Usage: wormhole-rs[EXE] [OPTIONS] <COMMAND>

Commands:
send[..][aliases: tx]
receive[..][aliases: rx]
send-many[..]
forward[..]

Options:
-v, --verbose[..]
-h, --help[..]
-V, --version[..]

Run a subcommand with `--help` to know how it's used.
To send files, use `wormhole send <PATH>`.
To receive files, use `wormhole receive <CODE>`.
Empty file added cli/tests/cmd/basic.stdout
Empty file.
2 changes: 2 additions & 0 deletions cli/tests/cmd/basic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin.name = "wormhole-rs"
status.code = 2
10 changes: 10 additions & 0 deletions cli/tests/cmd/forward.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Forward ports from one machine to another

Usage: wormhole-rs[EXE] forward [OPTIONS] <COMMAND>

Commands:
serve[..]
connect[..]

Options:
...
Empty file added cli/tests/cmd/forward.stdout
Empty file.
3 changes: 3 additions & 0 deletions cli/tests/cmd/forward.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
bin.name = "wormhole-rs"
args = "forward"
status.code = 2
Empty file added cli/tests/cmd/help.stderr
Empty file.
16 changes: 16 additions & 0 deletions cli/tests/cmd/help.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Usage: wormhole-rs[EXE] [OPTIONS] <COMMAND>

Commands:
send[..][aliases: tx]
receive[..][aliases: rx]
send-many[..]
forward[..]

Options:
-v, --verbose[..]
-h, --help[..]
-V, --version[..]

Run a subcommand with `--help` to know how it's used.
To send files, use `wormhole send <PATH>`.
To receive files, use `wormhole receive <CODE>`.
2 changes: 2 additions & 0 deletions cli/tests/cmd/help.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin.name = "wormhole-rs"
args = "--help"
Empty file.
9 changes: 9 additions & 0 deletions cli/tests/cmd/receive-help.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Receive a file or a folder

Usage: wormhole-rs[EXE] receive [OPTIONS] [CODE]

Arguments:
[CODE] Provide the code now rather than typing it interactively

Options:
...
2 changes: 2 additions & 0 deletions cli/tests/cmd/receive-help.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin.name = "wormhole-rs"
args = "receive --help"
Empty file added cli/tests/cmd/send-help.stderr
Empty file.
Loading
Loading