Better command line parsing, version, usage

Also tests for same
This commit is contained in:
John Doty 2023-01-24 09:13:53 -08:00
parent 52fe5523b2
commit fc144162b9
3 changed files with 132 additions and 11 deletions

7
Cargo.lock generated
View file

@ -256,6 +256,7 @@ dependencies = [
"bytes", "bytes",
"crossterm", "crossterm",
"home", "home",
"indoc",
"log", "log",
"open", "open",
"procfs", "procfs",
@ -328,6 +329,12 @@ dependencies = [
"cxx-build", "cxx-build",
] ]
[[package]]
name = "indoc"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780"
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.3" version = "1.0.3"

View file

@ -18,6 +18,7 @@ anyhow = "1.0"
bytes = "1" bytes = "1"
crossterm = { version = "0.25", features = ["event-stream"] } crossterm = { version = "0.25", features = ["event-stream"] }
home = "0.5.4" home = "0.5.4"
indoc = "1"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
open = "3" open = "3"
thiserror = "1.0" thiserror = "1.0"

View file

@ -1,19 +1,132 @@
// TODO: An actual proper UI. // TODO: An actual proper command line parsing
use indoc::indoc;
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn usage() {
println!(indoc! {"
usage: fwd [--version] (<server> | browse <url>)
To connect a client to a server that has an `fwd` installed in its path, run
`fwd <server>` on the client, where <server> is the name of the server to
connect to.
On a server that already has a client connected to it you can use `fwd browse
<url>` to open `<url>` in the default browser of the client.
"});
}
#[derive(Debug)]
enum Args {
Help,
Version,
Server,
Client(String),
Browse(String),
Error,
}
fn parse_args(args: Vec<String>) -> Args {
// Look for help; allow it to come anywhere because sometimes you just
// want to jam it on the end of an existing command line.
for arg in &args {
if arg == "--help" || arg == "-?" || arg == "-h" {
return Args::Help;
}
}
// No help, parse for reals.
if args.len() >= 2 && args[1] == "--version" {
Args::Version
} else if args.len() == 2 && &args[1] == "--server" {
Args::Server
} else if args.len() == 3 && args[1] == "browse" {
Args::Browse(args[2].to_string())
} else {
if args.len() != 2 {
Args::Error
} else {
Args::Client(args[1].to_string())
}
}
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let args: Vec<String> = std::env::args().collect(); match parse_args(std::env::args().collect()) {
Args::Help => {
if args.len() == 2 && &args[1] == "--server" { usage();
fwd::run_server().await; }
} else if args.len() == 3 && args[1] == "browse" { Args::Version => {
fwd::browse_url(&args[2]).await; println!("fwd {VERSION}");
} else { }
if args.len() < 2 { Args::Server => {
eprintln!("Usage: fwd <server>"); fwd::run_server().await;
}
Args::Browse(url) => {
fwd::browse_url(&url).await;
}
Args::Client(server) => {
fwd::run_client(&server).await;
}
Args::Error => {
usage();
std::process::exit(1); std::process::exit(1);
} }
};
}
fwd::run_client(&args[1]).await; #[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
// Goldarn it.
fn args(x: &[&str]) -> Vec<String> {
let mut vec: Vec<String> =
x.into_iter().map(|a| a.to_string()).collect();
vec.insert(0, "fwd".to_string());
vec
}
macro_rules! assert_arg_parse {
( $x:expr, $p:pat ) => {
assert_matches!(parse_args(args($x)), $p)
};
}
#[test]
fn help() {
assert_arg_parse!(&["--help"], Args::Help);
assert_arg_parse!(&["browse", "--help"], Args::Help);
assert_arg_parse!(&["foo.com", "--help"], Args::Help);
assert_arg_parse!(&["--help", "foo.com"], Args::Help);
assert_arg_parse!(&["browse", "-?"], Args::Help);
assert_arg_parse!(&["foo.com", "-h"], Args::Help);
}
#[test]
fn errors() {
assert_arg_parse!(&[], Args::Error);
assert_arg_parse!(&["browse", "google.com", "what"], Args::Error);
assert_arg_parse!(&["a", "b"], Args::Error);
assert_arg_parse!(&["--server", "something"], Args::Error);
}
#[test]
fn client() {
assert_arg_parse!(&["foo.com"], Args::Client(_));
assert_arg_parse!(&["a"], Args::Client(_));
assert_arg_parse!(&["browse"], Args::Client(_));
}
#[test]
fn server() {
assert_arg_parse!(&["--server"], Args::Server);
}
#[test]
fn browse() {
assert_arg_parse!(&["browse", "google.com"], Args::Browse(_));
} }
} }