Refactor argument parsing logic

This commit is contained in:
a2x 2024-04-21 00:30:00 +10:00
parent bda7f86ef3
commit 13b764854e
2 changed files with 59 additions and 108 deletions

View File

@ -8,7 +8,7 @@ repository = "https://github.com/a2x/cs2-dumper"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
clap = { version = "4.5", features = ["cargo"] } clap = { version = "4.5", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
heck = "0.5" heck = "0.5"
log = "0.4" log = "0.4"

View File

@ -1,4 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::time::Instant; use std::time::Instant;
use clap::*; use clap::*;
@ -18,107 +19,47 @@ mod mem;
mod output; mod output;
mod source2; mod source2;
const PROCESS_NAME: &str = "cs2.exe"; #[derive(Debug, Parser)]
#[command(author, version)]
struct Args {
/// The name of the memflow connector to use.
#[arg(short, long)]
connector: Option<String>,
/// Additional arguments to pass to the memflow connector.
#[arg(short = 'a', long)]
connector_args: Option<String>,
/// The types of files to generate.
#[arg(short, long, value_delimiter = ',', default_values = ["cs", "hpp", "json", "rs"])]
file_types: Vec<String>,
/// The number of spaces to use per indentation level.
#[arg(short, long, default_value_t = 4)]
indent_size: usize,
/// The output directory to write the generated files to.
#[arg(short, long, default_value = "output")]
output: PathBuf,
/// The name of the game process.
#[arg(short, long, default_value = "cs2.exe")]
process_name: String,
/// Increase logging verbosity. Can be specified multiple times.
#[arg(short, action = ArgAction::Count)]
verbose: u8,
}
fn main() -> Result<()> { fn main() -> Result<()> {
let args = Args::parse();
let now = Instant::now(); let now = Instant::now();
let matches = parse_args(); let log_level = match args.verbose {
let (conn_name, conn_args, file_types, indent_size, out_dir) = extract_args(&matches)?;
let os = if let Some(conn_name) = conn_name {
let inventory = Inventory::scan();
inventory
.builder()
.connector(&conn_name)
.args(conn_args)
.os("win32")
.build()?
} else {
// Fallback to the native OS layer if no connector name was specified.
memflow_native::create_os(&Default::default(), Default::default())?
};
let mut process = os.into_process_by_name(PROCESS_NAME)?;
let result = analysis::analyze_all(&mut process)?;
let output = Output::new(&file_types, indent_size, &out_dir, &result)?;
output.dump_all(&mut process)?;
info!("finished in {:?}", now.elapsed());
Ok(())
}
fn parse_args() -> ArgMatches {
Command::new("cs2-dumper")
.version(crate_version!())
.author(crate_authors!())
.arg(
Arg::new("connector")
.help("The name of the memflow connector to use.")
.long("connector")
.short('c')
.required(false),
)
.arg(
Arg::new("connector-args")
.help("Additional arguments to pass to the connector.")
.long("connector-args")
.short('a')
.required(false),
)
.arg(
Arg::new("file-types")
.help("The types of files to generate.")
.long("file-types")
.short('f')
.action(ArgAction::Append)
.default_values(["cs", "hpp", "json", "rs"])
.value_parser(["cs", "hpp", "json", "rs"])
.value_delimiter(',')
.required(false),
)
.arg(
Arg::new("indent-size")
.help("The number of spaces to use per indentation level.")
.long("indent-size")
.short('i')
.default_value("4")
.value_parser(value_parser!(usize))
.required(false),
)
.arg(
Arg::new("output")
.help("The output directory to write the generated files to.")
.long("output")
.short('o')
.default_value("output")
.value_parser(value_parser!(PathBuf))
.required(false),
)
.arg(
Arg::new("verbose")
.help("Increase logging verbosity. Can be specified multiple times.")
.short('v')
.action(ArgAction::Count),
)
.get_matches()
}
fn extract_args(
matches: &ArgMatches,
) -> Result<(Option<String>, ConnectorArgs, Vec<String>, usize, &PathBuf)> {
use std::str::FromStr;
let log_level = match matches.get_count("verbose") {
0 => Level::Error, 0 => Level::Error,
1 => Level::Warn, 1 => Level::Warn,
2 => Level::Info, 2 => Level::Info,
3 => Level::Debug, 3 => Level::Debug,
4 => Level::Trace,
_ => Level::Trace, _ => Level::Trace,
}; };
@ -130,23 +71,33 @@ fn extract_args(
) )
.unwrap(); .unwrap();
let conn_name = matches let conn_args = args
.get_one::<String>("connector") .connector_args
.map(|s| s.to_string());
let conn_args = matches
.get_one::<String>("connector-args")
.map(|s| ConnectorArgs::from_str(&s).expect("unable to parse connector arguments")) .map(|s| ConnectorArgs::from_str(&s).expect("unable to parse connector arguments"))
.unwrap_or_default(); .unwrap_or_default();
let file_types = matches let os = if let Some(conn) = args.connector {
.get_many::<String>("file-types") let inventory = Inventory::scan();
.unwrap()
.map(|s| s.to_string())
.collect();
let indent_size = *matches.get_one::<usize>("indent-size").unwrap(); inventory
let out_dir = matches.get_one::<PathBuf>("output").unwrap(); .builder()
.connector(&conn)
.args(conn_args)
.os("win32")
.build()?
} else {
// Fallback to the native OS layer if no connector name was specified.
memflow_native::create_os(&Default::default(), Default::default())?
};
Ok((conn_name, conn_args, file_types, indent_size, out_dir)) let mut process = os.into_process_by_name(&args.process_name)?;
let result = analysis::analyze_all(&mut process)?;
let output = Output::new(&args.file_types, args.indent_size, &args.output, &result)?;
output.dump_all(&mut process)?;
info!("finished in {:?}", now.elapsed());
Ok(())
} }