Quick sketch adding derived cmd line arg parsing to veilid-cli
Quick sketch showing cmd line args via clap-derive (same underlying parser, but simplifies cmd line parsing logic and reduces surface area for potential bugs)
This commit is contained in:
		
							
								
								
									
										117
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										117
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -154,6 +154,55 @@ dependencies = [ | |||||||
|  "winapi", |  "winapi", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anstream" | ||||||
|  | version = "0.3.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" | ||||||
|  | dependencies = [ | ||||||
|  |  "anstyle", | ||||||
|  |  "anstyle-parse", | ||||||
|  |  "anstyle-query", | ||||||
|  |  "anstyle-wincon", | ||||||
|  |  "colorchoice", | ||||||
|  |  "is-terminal", | ||||||
|  |  "utf8parse", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anstyle" | ||||||
|  | version = "1.0.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anstyle-parse" | ||||||
|  | version = "0.2.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" | ||||||
|  | dependencies = [ | ||||||
|  |  "utf8parse", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anstyle-query" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" | ||||||
|  | dependencies = [ | ||||||
|  |  "windows-sys 0.48.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "anstyle-wincon" | ||||||
|  | version = "1.0.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" | ||||||
|  | dependencies = [ | ||||||
|  |  "anstyle", | ||||||
|  |  "windows-sys 0.48.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "anyhow" | name = "anyhow" | ||||||
| version = "1.0.72" | version = "1.0.72" | ||||||
| @@ -966,13 +1015,48 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "atty", |  "atty", | ||||||
|  "bitflags 1.3.2", |  "bitflags 1.3.2", | ||||||
|  "clap_lex", |  "clap_lex 0.2.4", | ||||||
|  "indexmap 1.9.3", |  "indexmap 1.9.3", | ||||||
|  "strsim 0.10.0", |  "strsim 0.10.0", | ||||||
|  "termcolor", |  "termcolor", | ||||||
|  "textwrap 0.16.0", |  "textwrap 0.16.0", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "clap" | ||||||
|  | version = "4.3.21" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" | ||||||
|  | dependencies = [ | ||||||
|  |  "clap_builder", | ||||||
|  |  "clap_derive", | ||||||
|  |  "once_cell", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "clap_builder" | ||||||
|  | version = "4.3.21" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" | ||||||
|  | dependencies = [ | ||||||
|  |  "anstream", | ||||||
|  |  "anstyle", | ||||||
|  |  "clap_lex 0.5.0", | ||||||
|  |  "strsim 0.10.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "clap_derive" | ||||||
|  | version = "4.3.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" | ||||||
|  | dependencies = [ | ||||||
|  |  "heck", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn 2.0.27", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "clap_lex" | name = "clap_lex" | ||||||
| version = "0.2.4" | version = "0.2.4" | ||||||
| @@ -982,6 +1066,12 @@ dependencies = [ | |||||||
|  "os_str_bytes", |  "os_str_bytes", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "clap_lex" | ||||||
|  | version = "0.5.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "clipboard-win" | name = "clipboard-win" | ||||||
| version = "4.5.0" | version = "4.5.0" | ||||||
| @@ -1021,6 +1111,12 @@ version = "1.1.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "colorchoice" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "combine" | name = "combine" | ||||||
| version = "4.6.6" | version = "4.6.6" | ||||||
| @@ -2552,6 +2648,17 @@ version = "2.8.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" | checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "is-terminal" | ||||||
|  | version = "0.4.9" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" | ||||||
|  | dependencies = [ | ||||||
|  |  "hermit-abi 0.3.2", | ||||||
|  |  "rustix 0.38.4", | ||||||
|  |  "windows-sys 0.48.0", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "itertools" | name = "itertools" | ||||||
| version = "0.10.5" | version = "0.10.5" | ||||||
| @@ -5622,6 +5729,12 @@ version = "0.7.6" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" | checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "utf8parse" | ||||||
|  | version = "0.2.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "valuable" | name = "valuable" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
| @@ -5655,7 +5768,7 @@ dependencies = [ | |||||||
|  "async-tungstenite 0.8.0", |  "async-tungstenite 0.8.0", | ||||||
|  "bugsalot", |  "bugsalot", | ||||||
|  "cfg-if 1.0.0", |  "cfg-if 1.0.0", | ||||||
|  "clap 3.2.25", |  "clap 4.3.21", | ||||||
|  "config", |  "config", | ||||||
|  "crossbeam-channel", |  "crossbeam-channel", | ||||||
|  "cursive", |  "cursive", | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ cursive_buffered_backend = { path = "../external/cursive_buffered_backend" } | |||||||
| cursive_table_view = "0.14.0" | cursive_table_view = "0.14.0" | ||||||
| arboard = "3.2.0" | arboard = "3.2.0" | ||||||
| # cursive-tabs = "0.5.0" | # cursive-tabs = "0.5.0" | ||||||
| clap = "^3" | clap = {version= "4", features = ["derive"]} | ||||||
| directories = "^4" | directories = "^4" | ||||||
| log = "^0" | log = "^0" | ||||||
| futures = "^0" | futures = "^0" | ||||||
|   | |||||||
| @@ -4,11 +4,9 @@ | |||||||
|  |  | ||||||
| use crate::tools::*; | use crate::tools::*; | ||||||
|  |  | ||||||
| use clap::{Arg, ColorChoice, Command}; | use clap::{Parser, ValueEnum}; | ||||||
| use flexi_logger::*; | use flexi_logger::*; | ||||||
| use std::ffi::OsStr; | use std::{net::ToSocketAddrs, path::PathBuf}; | ||||||
| use std::net::ToSocketAddrs; |  | ||||||
| use std::path::Path; |  | ||||||
|  |  | ||||||
| mod client_api_connection; | mod client_api_connection; | ||||||
| mod command_processor; | mod command_processor; | ||||||
| @@ -17,62 +15,45 @@ mod settings; | |||||||
| mod tools; | mod tools; | ||||||
| mod ui; | mod ui; | ||||||
|  |  | ||||||
| fn parse_command_line(default_config_path: &OsStr) -> Result<clap::ArgMatches, String> { | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] | ||||||
|     let matches = Command::new("veilid-cli") | enum LogLevel { | ||||||
|         .version("0.1") |     /// Turn on debug logging | ||||||
|         .color(ColorChoice::Auto) |     Debug, | ||||||
|         .about("Veilid Console Client") |     /// Turn on trace logging | ||||||
|         .arg( |     Trace, | ||||||
|             Arg::new("address") | } | ||||||
|                 .required(false) |  | ||||||
|                 .help("Address to connect to"), |  | ||||||
|         ) |  | ||||||
|         .arg( |  | ||||||
|             Arg::new("debug") |  | ||||||
|                 .long("debug") |  | ||||||
|                 .help("Turn on debug logging"), |  | ||||||
|         ) |  | ||||||
|         .arg( |  | ||||||
|             Arg::new("wait-for-debug") |  | ||||||
|                 .long("wait-for-debug") |  | ||||||
|                 .help("Wait for debugger to attach"), |  | ||||||
|         ) |  | ||||||
|         .arg( |  | ||||||
|             Arg::new("trace") |  | ||||||
|                 .long("trace") |  | ||||||
|                 .conflicts_with("debug") |  | ||||||
|                 .help("Turn on trace logging"), |  | ||||||
|         ) |  | ||||||
|         .arg( |  | ||||||
|             Arg::new("config-file") |  | ||||||
|                 .short('c') |  | ||||||
|                 .takes_value(true) |  | ||||||
|                 .value_name("FILE") |  | ||||||
|                 .default_value_os(default_config_path) |  | ||||||
|                 .allow_invalid_utf8(true) |  | ||||||
|                 .help("Specify a configuration file to use"), |  | ||||||
|         ) |  | ||||||
|         .get_matches(); |  | ||||||
|  |  | ||||||
|     Ok(matches) | #[derive(Parser, Debug)] | ||||||
|  | #[command(author, version, about = "Veilid Console Client")] | ||||||
|  | struct CmdlineArgs { | ||||||
|  |     /// Address to connect to | ||||||
|  |     #[arg(long)] | ||||||
|  |     address: Option<String>, | ||||||
|  |     /// Wait for debugger to attach | ||||||
|  |     #[arg(long)] | ||||||
|  |     wait_for_debug: bool, | ||||||
|  |     /// Specify a configuration file to use | ||||||
|  |     #[arg(short, long, value_name = "FILE", /*allow_invalid_utf8 = true*/)] | ||||||
|  |     config_file: Option<PathBuf>, | ||||||
|  |     /// log level | ||||||
|  |     #[arg(value_enum)] | ||||||
|  |     log_level: Option<LogLevel>, | ||||||
| } | } | ||||||
|  |  | ||||||
| fn main() -> Result<(), String> { | fn main() -> Result<(), String> { | ||||||
|     // Get command line options |     // Get command line options | ||||||
|     let default_config_path = settings::Settings::get_default_config_path(); |     let default_config_path = settings::Settings::get_default_config_path(); | ||||||
|     let matches = parse_command_line(default_config_path.as_os_str())?; |     let args = CmdlineArgs::parse(); | ||||||
|     if matches.occurrences_of("wait-for-debug") != 0 { |  | ||||||
|  |     if args.wait_for_debug { | ||||||
|         use bugsalot::debugger; |         use bugsalot::debugger; | ||||||
|         debugger::wait_until_attached(None).expect("state() not implemented on this platform"); |         debugger::wait_until_attached(None).expect("state() not implemented on this platform"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Attempt to load configuration |     // Attempt to load configuration | ||||||
|     let settings_path = if let Some(config_file) = matches.value_of_os("config-file") { |     let settings_path = args.config_file.unwrap_or(default_config_path); | ||||||
|         if Path::new(config_file).exists() { |     let settings_path = if settings_path.exists() { | ||||||
|             Some(config_file) |         Some(settings_path.into_os_string()) | ||||||
|         } else { |  | ||||||
|             None |  | ||||||
|         } |  | ||||||
|     } else { |     } else { | ||||||
|         None |         None | ||||||
|     }; |     }; | ||||||
| @@ -81,11 +62,11 @@ fn main() -> Result<(), String> { | |||||||
|         .map_err(|e| format!("configuration is invalid: {}", e))?; |         .map_err(|e| format!("configuration is invalid: {}", e))?; | ||||||
|  |  | ||||||
|     // Set config from command line |     // Set config from command line | ||||||
|     if matches.occurrences_of("debug") != 0 { |     if let Some(LogLevel::Debug) = args.log_level { | ||||||
|         settings.logging.level = settings::LogLevel::Debug; |         settings.logging.level = settings::LogLevel::Debug; | ||||||
|         settings.logging.terminal.enabled = true; |         settings.logging.terminal.enabled = true; | ||||||
|     } |     } | ||||||
|     if matches.occurrences_of("trace") != 0 { |     if let Some(LogLevel::Trace) = args.log_level { | ||||||
|         settings.logging.level = settings::LogLevel::Trace; |         settings.logging.level = settings::LogLevel::Trace; | ||||||
|         settings.logging.terminal.enabled = true; |         settings.logging.terminal.enabled = true; | ||||||
|     } |     } | ||||||
| @@ -142,7 +123,7 @@ fn main() -> Result<(), String> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     // Get client address |     // Get client address | ||||||
|     let server_addrs = if let Some(address_arg) = matches.value_of("address") { |     let server_addrs = if let Some(address_arg) = args.address { | ||||||
|         address_arg |         address_arg | ||||||
|             .to_socket_addrs() |             .to_socket_addrs() | ||||||
|             .map_err(|e| format!("Invalid server address '{}'", e))? |             .map_err(|e| format!("Invalid server address '{}'", e))? | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use directories::*; | use directories::*; | ||||||
|  |  | ||||||
| use serde_derive::*; | use serde_derive::*; | ||||||
| use std::ffi::OsStr; | use std::ffi::OsString; | ||||||
| use std::net::{SocketAddr, ToSocketAddrs}; | use std::net::{SocketAddr, ToSocketAddrs}; | ||||||
| use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||||
|  |  | ||||||
| @@ -234,13 +234,13 @@ impl Settings { | |||||||
|         default_log_directory |         default_log_directory | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn new(config_file: Option<&OsStr>) -> Result<Self, config::ConfigError> { |     pub fn new(config_file: Option<OsString>) -> Result<Self, config::ConfigError> { | ||||||
|         // Load the default config |         // Load the default config | ||||||
|         let mut cfg = load_default_config()?; |         let mut cfg = load_default_config()?; | ||||||
|  |  | ||||||
|         // Merge in the config file if we have one |         // Merge in the config file if we have one | ||||||
|         if let Some(config_file) = config_file { |         if let Some(config_file) = config_file { | ||||||
|             let config_file_path = Path::new(config_file); |             let config_file_path = Path::new(&config_file); | ||||||
|             // If the user specifies a config file on the command line then it must exist |             // If the user specifies a config file on the command line then it must exist | ||||||
|             cfg = load_config(cfg, config_file_path)?; |             cfg = load_config(cfg, config_file_path)?; | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user