From 9efc1fc66a24d8d554083e39cbe9eeac66d834f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Thu, 13 Aug 2020 00:13:01 +0200 Subject: [PATCH] add aquatic crate with master executable, refactor cli_helpers --- Cargo.lock | 32 +++----- Cargo.toml | 1 + aquatic/Cargo.toml | 16 ++++ aquatic/src/main.rs | 99 +++++++++++++++++++++++++ aquatic_cli_helpers/Cargo.toml | 1 - aquatic_cli_helpers/src/lib.rs | 115 +++++++++++++++++++++-------- aquatic_http/src/bin/main.rs | 5 +- aquatic_http/src/lib/lib.rs | 3 + aquatic_http_load_test/src/main.rs | 3 +- aquatic_udp/src/bin/aquatic_udp.rs | 3 +- aquatic_udp/src/lib/lib.rs | 3 + aquatic_udp_bench/src/main.rs | 5 +- aquatic_udp_load_test/src/main.rs | 3 +- aquatic_ws/src/bin/main.rs | 5 +- aquatic_ws/src/lib/lib.rs | 3 + aquatic_ws_load_test/src/main.rs | 3 +- scripts/run-aquatic.sh | 5 ++ 17 files changed, 243 insertions(+), 62 deletions(-) create mode 100644 aquatic/Cargo.toml create mode 100644 aquatic/src/main.rs create mode 100755 scripts/run-aquatic.sh diff --git a/Cargo.lock b/Cargo.lock index 57bf952..ec3f860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,12 +36,22 @@ version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +[[package]] +name = "aquatic" +version = "0.1.0" +dependencies = [ + "aquatic_cli_helpers", + "aquatic_http", + "aquatic_udp", + "aquatic_ws", + "mimalloc", +] + [[package]] name = "aquatic_cli_helpers" version = "0.1.0" dependencies = [ "anyhow", - "gumdrop", "serde", "simplelog", "toml", @@ -739,26 +749,6 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" -[[package]] -name = "gumdrop" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b" -dependencies = [ - "gumdrop_derive", -] - -[[package]] -name = "gumdrop_derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "half" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 5011614..54f45a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ + "aquatic", "aquatic_cli_helpers", "aquatic_common", "aquatic_http", diff --git a/aquatic/Cargo.toml b/aquatic/Cargo.toml new file mode 100644 index 0000000..45c9836 --- /dev/null +++ b/aquatic/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "aquatic" +version = "0.1.0" +authors = ["Joakim FrostegÄrd "] +edition = "2018" +license = "Apache-2.0" + +[[bin]] +name = "aquatic" + +[dependencies] +aquatic_cli_helpers = { path = "../aquatic_cli_helpers" } +aquatic_http = { path = "../aquatic_http" } +aquatic_udp = { path = "../aquatic_udp" } +aquatic_ws = { path = "../aquatic_ws" } +mimalloc = { version = "0.1", default-features = false } \ No newline at end of file diff --git a/aquatic/src/main.rs b/aquatic/src/main.rs new file mode 100644 index 0000000..33f0039 --- /dev/null +++ b/aquatic/src/main.rs @@ -0,0 +1,99 @@ +use aquatic_cli_helpers::{Options, run_app_with_cli_and_config, print_help}; +use aquatic_http::config::Config as HttpConfig; +use aquatic_udp::config::Config as UdpConfig; +use aquatic_ws::config::Config as WsConfig; + + +#[global_allocator] +static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + + +const APP_NAME: &str = "aquatic: BitTorrent tracker"; + + +fn main(){ + ::std::process::exit(match run(){ + Ok(()) => 0, + Err(None) => { + print_help(|| gen_info(), None); + + 0 + }, + Err(opt_err@Some(_)) => { + print_help(|| gen_info(), opt_err); + + 1 + }, + }) +} + + +fn run() -> Result<(), Option>{ + let mut arg_iter = ::std::env::args().skip(1); + + let protocol = if let Some(protocol) = arg_iter.next(){ + protocol + } else { + return Err(None); + }; + + let options = match Options::parse_args(arg_iter){ + Ok(options) => options, + Err(opt_err) => { + return Err(opt_err); + } + }; + + match protocol.as_str() { + "udp" => { + run_app_with_cli_and_config::( + aquatic_udp::APP_NAME, + aquatic_udp::run, + Some(options), + ) + }, + "http" => { + run_app_with_cli_and_config::( + aquatic_http::APP_NAME, + aquatic_http::run, + Some(options), + ) + }, + "ws" => { + run_app_with_cli_and_config::( + aquatic_ws::APP_NAME, + aquatic_ws::run, + Some(options), + ) + }, + arg => { + let opt_err = if arg == "-h" || arg == "--help" { + None + } else if arg.chars().next() == Some('-'){ + Some("First argument must be protocol".to_string()) + } else { + Some("Invalid protocol".to_string()) + }; + + return Err(opt_err) + }, + } + + Ok(()) +} + + +fn gen_info() -> String { + let mut info = String::new(); + + info.push_str(APP_NAME); + + let app_path = ::std::env::args().next().unwrap(); + info.push_str(&format!("\n\nUsage: {} PROTOCOL [OPTIONS]", app_path)); + info.push_str("\n\nAvailable protocols:"); + info.push_str("\n udp"); + info.push_str("\n http"); + info.push_str("\n ws"); + + info +} \ No newline at end of file diff --git a/aquatic_cli_helpers/Cargo.toml b/aquatic_cli_helpers/Cargo.toml index 830a6a1..be5ac9a 100644 --- a/aquatic_cli_helpers/Cargo.toml +++ b/aquatic_cli_helpers/Cargo.toml @@ -7,7 +7,6 @@ license = "Apache-2.0" [dependencies] anyhow = "1" -gumdrop = "0.8" serde = { version = "1", features = ["derive"] } simplelog = "0.8" toml = "0.5" \ No newline at end of file diff --git a/aquatic_cli_helpers/src/lib.rs b/aquatic_cli_helpers/src/lib.rs index 739cfbc..cc8ab57 100644 --- a/aquatic_cli_helpers/src/lib.rs +++ b/aquatic_cli_helpers/src/lib.rs @@ -2,7 +2,6 @@ use std::fs::File; use std::io::Read; use anyhow::Context; -use gumdrop::Options; use serde::{Serialize, Deserialize, de::DeserializeOwned}; use simplelog::{ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; @@ -33,23 +32,60 @@ pub trait Config: Default + Serialize + DeserializeOwned { } -#[derive(Debug, Options)] -struct AppOptions { - #[options(help = "run with config file", short = "c", meta = "PATH")] +#[derive(Debug, Default)] +pub struct Options { config_file: Option, - #[options(help = "print default config file", short = "p")] print_config: bool, - #[options(help = "print help message")] - help: bool, +} + + +impl Options { + pub fn parse_args( + mut arg_iter: I + ) -> Result> + where I: Iterator + { + let mut options = Options::default(); + + loop { + if let Some(arg) = arg_iter.next(){ + match arg.as_str(){ + "-c" | "--config-file" => { + if let Some(path) = arg_iter.next(){ + options.config_file = Some(path); + } else { + return Err( + Some("No config file path given".to_string()) + ); + } + }, + "-p" | "--print-config" => { + options.print_config = true; + }, + "-h" | "--help" => { + return Err(None); + }, + _ => { + return Err(Some("Unrecognized argument".to_string())); + } + } + } else { + break; + } + } + + Ok(options) + } } pub fn run_app_with_cli_and_config( - title: &str, + app_title: &str, // Function that takes config file and runs application app_fn: fn(T) -> anyhow::Result<()>, + opts: Option, ) where T: Config { - ::std::process::exit(match run_inner(title, app_fn) { + ::std::process::exit(match run_inner(app_title, app_fn, opts) { Ok(()) => 0, Err(err) => { eprintln!("Error: {:#}", err); @@ -61,32 +97,45 @@ pub fn run_app_with_cli_and_config( fn run_inner( - title: &str, + app_title: &str, // Function that takes config file and runs application app_fn: fn(T) -> anyhow::Result<()>, + // Possibly preparsed options + options: Option, ) -> anyhow::Result<()> where T: Config { - let args: Vec = ::std::env::args().collect(); + let options = if let Some(options) = options { + options + } else { + let mut arg_iter = ::std::env::args(); - let opts = AppOptions::parse_args_default(&args[1..])?; + let app_path = arg_iter.next().unwrap(); - if opts.help_requested(){ - print_help(title, None); + match Options::parse_args(arg_iter){ + Ok(options) => options, + Err(opt_err) => { + let gen_info = || format!( + "{}\n\nUsage: {} [OPTIONS]", + app_title, + app_path + ); - Ok(()) - } else if opts.print_config { + print_help(gen_info, opt_err); + + return Ok(()) + } + } + }; + + if options.print_config { print!("{}", default_config_as_toml::()); Ok(()) - } else if let Some(config_file) = opts.config_file { - let config: T = config_from_toml_file(config_file)?; - - if let Some(log_level) = config.get_log_level(){ - start_logger(log_level)?; - } - - app_fn(config) } else { - let config = T::default(); + let config = if let Some(path) = options.config_file { + config_from_toml_file(path)? + } else { + T::default() + }; if let Some(log_level) = config.get_log_level(){ start_logger(log_level)?; @@ -97,14 +146,20 @@ fn run_inner( } -fn print_help(title: &str, opt_error: Option){ - println!("{}", title); +pub fn print_help( + info_generator: F, + opt_error: Option +) where F: FnOnce() -> String { + println!("{}", info_generator()); + + println!("\nOptions:"); + println!(" -c, --config-file Load config from this path"); + println!(" -p, --print-config Print default config"); + println!(" -h, --help Print this help message"); if let Some(error) = opt_error { - println!("\nError: {:#}.", error); + println!("\nError: {}.", error); } - - println!("\n{}", AppOptions::usage()); } diff --git a/aquatic_http/src/bin/main.rs b/aquatic_http/src/bin/main.rs index 0b049cf..36cef6b 100644 --- a/aquatic_http/src/bin/main.rs +++ b/aquatic_http/src/bin/main.rs @@ -8,7 +8,8 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; fn main(){ run_app_with_cli_and_config::( - "aquatic: BitTorrent (HTTP/TLS) tracker", - aquatic_http::run + aquatic_http::APP_NAME, + aquatic_http::run, + None ) } \ No newline at end of file diff --git a/aquatic_http/src/lib/lib.rs b/aquatic_http/src/lib/lib.rs index 979dc87..e0d3179 100644 --- a/aquatic_http/src/lib/lib.rs +++ b/aquatic_http/src/lib/lib.rs @@ -18,6 +18,9 @@ use config::Config; use network::utils::create_tls_acceptor; +pub const APP_NAME: &str = "aquatic_http: HTTP/TLS BitTorrent tracker"; + + pub fn run(config: Config) -> anyhow::Result<()> { let opt_tls_acceptor = create_tls_acceptor(&config.network.tls)?; diff --git a/aquatic_http_load_test/src/main.rs b/aquatic_http_load_test/src/main.rs index aade6c9..6e6af6c 100644 --- a/aquatic_http_load_test/src/main.rs +++ b/aquatic_http_load_test/src/main.rs @@ -25,8 +25,9 @@ const MBITS_FACTOR: f64 = 1.0 / ((1024.0 * 1024.0) / 8.0); pub fn main(){ aquatic_cli_helpers::run_app_with_cli_and_config::( - "aquatic http bittorrent tracker: load tester", + "aquatic_http_load_test: BitTorrent load tester", run, + None ) } diff --git a/aquatic_udp/src/bin/aquatic_udp.rs b/aquatic_udp/src/bin/aquatic_udp.rs index 001b28e..b520bc7 100644 --- a/aquatic_udp/src/bin/aquatic_udp.rs +++ b/aquatic_udp/src/bin/aquatic_udp.rs @@ -4,7 +4,8 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; fn main(){ aquatic_cli_helpers::run_app_with_cli_and_config::( - "aquatic: udp bittorrent tracker", + aquatic_udp::APP_NAME, aquatic_udp::run, + None ) } \ No newline at end of file diff --git a/aquatic_udp/src/lib/lib.rs b/aquatic_udp/src/lib/lib.rs index 4c4d2c9..012be59 100644 --- a/aquatic_udp/src/lib/lib.rs +++ b/aquatic_udp/src/lib/lib.rs @@ -15,6 +15,9 @@ use config::Config; use common::State; +pub const APP_NAME: &str = "aquatic_udp: UDP BitTorrent tracker"; + + pub fn run(config: Config) -> ::anyhow::Result<()> { let state = State::default(); diff --git a/aquatic_udp_bench/src/main.rs b/aquatic_udp_bench/src/main.rs index 9903912..1bc80c3 100644 --- a/aquatic_udp_bench/src/main.rs +++ b/aquatic_udp_bench/src/main.rs @@ -39,8 +39,9 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; fn main(){ run_app_with_cli_and_config::( - "aquatic udp bittorrent tracker: benchmarker", - run + "aquatic_udp_bench: Run aquatic_udp benchmarks", + run, + None ) } diff --git a/aquatic_udp_load_test/src/main.rs b/aquatic_udp_load_test/src/main.rs index a70df74..3e79b39 100644 --- a/aquatic_udp_load_test/src/main.rs +++ b/aquatic_udp_load_test/src/main.rs @@ -26,8 +26,9 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; pub fn main(){ aquatic_cli_helpers::run_app_with_cli_and_config::( - "aquatic udp bittorrent tracker: load tester", + "aquatic_udp_load_test: BitTorrent load tester", run, + None, ) } diff --git a/aquatic_ws/src/bin/main.rs b/aquatic_ws/src/bin/main.rs index 1750f60..e26c94e 100644 --- a/aquatic_ws/src/bin/main.rs +++ b/aquatic_ws/src/bin/main.rs @@ -8,7 +8,8 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; fn main(){ run_app_with_cli_and_config::( - "aquatic: webtorrent tracker", - aquatic_ws::run + aquatic_ws::APP_NAME, + aquatic_ws::run, + None ) } \ No newline at end of file diff --git a/aquatic_ws/src/lib/lib.rs b/aquatic_ws/src/lib/lib.rs index fa057f0..3132b29 100644 --- a/aquatic_ws/src/lib/lib.rs +++ b/aquatic_ws/src/lib/lib.rs @@ -20,6 +20,9 @@ use common::*; use config::Config; +pub const APP_NAME: &str = "aquatic_ws: WebTorrent tracker"; + + pub fn run(config: Config) -> anyhow::Result<()> { let opt_tls_acceptor = create_tls_acceptor(&config)?; diff --git a/aquatic_ws_load_test/src/main.rs b/aquatic_ws_load_test/src/main.rs index 64d9685..f2c89d9 100644 --- a/aquatic_ws_load_test/src/main.rs +++ b/aquatic_ws_load_test/src/main.rs @@ -21,8 +21,9 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; pub fn main(){ aquatic_cli_helpers::run_app_with_cli_and_config::( - "aquatic: webtorrent tracker load tester", + "aquatic_ws_load_test: WebTorrent load tester", run, + None ) } diff --git a/scripts/run-aquatic.sh b/scripts/run-aquatic.sh new file mode 100755 index 0000000..fac0706 --- /dev/null +++ b/scripts/run-aquatic.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +. ./scripts/env-native-cpu-without-avx-512 + +cargo run --release --bin aquatic -- $@