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:
@@ -4,11 +4,9 @@
|
||||
|
||||
use crate::tools::*;
|
||||
|
||||
use clap::{Arg, ColorChoice, Command};
|
||||
use clap::{Parser, ValueEnum};
|
||||
use flexi_logger::*;
|
||||
use std::ffi::OsStr;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::path::Path;
|
||||
use std::{net::ToSocketAddrs, path::PathBuf};
|
||||
|
||||
mod client_api_connection;
|
||||
mod command_processor;
|
||||
@@ -17,62 +15,45 @@ mod settings;
|
||||
mod tools;
|
||||
mod ui;
|
||||
|
||||
fn parse_command_line(default_config_path: &OsStr) -> Result<clap::ArgMatches, String> {
|
||||
let matches = Command::new("veilid-cli")
|
||||
.version("0.1")
|
||||
.color(ColorChoice::Auto)
|
||||
.about("Veilid Console Client")
|
||||
.arg(
|
||||
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();
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
|
||||
enum LogLevel {
|
||||
/// Turn on debug logging
|
||||
Debug,
|
||||
/// Turn on trace logging
|
||||
Trace,
|
||||
}
|
||||
|
||||
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> {
|
||||
// Get command line options
|
||||
let default_config_path = settings::Settings::get_default_config_path();
|
||||
let matches = parse_command_line(default_config_path.as_os_str())?;
|
||||
if matches.occurrences_of("wait-for-debug") != 0 {
|
||||
let args = CmdlineArgs::parse();
|
||||
|
||||
if args.wait_for_debug {
|
||||
use bugsalot::debugger;
|
||||
debugger::wait_until_attached(None).expect("state() not implemented on this platform");
|
||||
}
|
||||
|
||||
// Attempt to load configuration
|
||||
let settings_path = if let Some(config_file) = matches.value_of_os("config-file") {
|
||||
if Path::new(config_file).exists() {
|
||||
Some(config_file)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let settings_path = args.config_file.unwrap_or(default_config_path);
|
||||
let settings_path = if settings_path.exists() {
|
||||
Some(settings_path.into_os_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -81,11 +62,11 @@ fn main() -> Result<(), String> {
|
||||
.map_err(|e| format!("configuration is invalid: {}", e))?;
|
||||
|
||||
// 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.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.terminal.enabled = true;
|
||||
}
|
||||
@@ -142,7 +123,7 @@ fn main() -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
// 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
|
||||
.to_socket_addrs()
|
||||
.map_err(|e| format!("Invalid server address '{}'", e))?
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use directories::*;
|
||||
|
||||
use serde_derive::*;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -234,13 +234,13 @@ impl Settings {
|
||||
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
|
||||
let mut cfg = load_default_config()?;
|
||||
|
||||
// Merge in the config file if we have one
|
||||
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
|
||||
cfg = load_config(cfg, config_file_path)?;
|
||||
}
|
||||
|
Reference in New Issue
Block a user