diff --git a/Cargo.lock b/Cargo.lock index d485a23..c25a015 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,6 +256,7 @@ dependencies = [ "bytes", "crossterm", "home", + "indoc", "log", "open", "procfs", @@ -328,6 +329,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "indoc" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2d6f23ffea9d7e76c53eee25dfb67bcd8fde7f1198b0855350698c9f07c780" + [[package]] name = "io-lifetimes" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index a475b86..b2479a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ anyhow = "1.0" bytes = "1" crossterm = { version = "0.25", features = ["event-stream"] } home = "0.5.4" +indoc = "1" log = { version = "0.4", features = ["std"] } open = "3" thiserror = "1.0" diff --git a/src/main.rs b/src/main.rs index ec31f5c..243f4f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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] ( | browse ) + +To connect a client to a server that has an `fwd` installed in its path, run +`fwd ` on the client, where 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 +` to open `` in the default browser of the client. + "}); +} + +#[derive(Debug)] +enum Args { + Help, + Version, + Server, + Client(String), + Browse(String), + Error, +} + +fn parse_args(args: Vec) -> 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] async fn main() { - let args: Vec = std::env::args().collect(); - - if args.len() == 2 && &args[1] == "--server" { - fwd::run_server().await; - } else if args.len() == 3 && args[1] == "browse" { - fwd::browse_url(&args[2]).await; - } else { - if args.len() < 2 { - eprintln!("Usage: fwd "); + match parse_args(std::env::args().collect()) { + Args::Help => { + usage(); + } + Args::Version => { + println!("fwd {VERSION}"); + } + Args::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); } + }; +} - fwd::run_client(&args[1]).await; +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + + // Goldarn it. + fn args(x: &[&str]) -> Vec { + let mut vec: Vec = + 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(_)); } }