From 86eefdb1f91b9b7f86a4eb2f4e51b2094eab313f Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Wed, 2 Jun 2021 00:08:28 +0800 Subject: [PATCH] feat: use clap subcommand --- src/args.rs | 42 -------------------- src/cmd.rs | 10 +++++ src/cmd_find.rs | 32 ++++++++++++++++ src/cmd_plist_default.rs | 82 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 68 ++++++++++----------------------- 5 files changed, 144 insertions(+), 90 deletions(-) delete mode 100644 src/args.rs create mode 100644 src/cmd.rs create mode 100644 src/cmd_find.rs create mode 100644 src/cmd_plist_default.rs diff --git a/src/args.rs b/src/args.rs deleted file mode 100644 index 816def7..0000000 --- a/src/args.rs +++ /dev/null @@ -1,42 +0,0 @@ -use clap::{ArgMatches, App, Arg}; - -pub enum PlistFormat { - Xml, - Binary, -} - -pub struct ParsedArgs { - pub in_file: String, - pub format: PlistFormat, - pub out_file: Option, - pub search: Option, -} - -pub fn parse_args(matches: ArgMatches<'static>) -> ParsedArgs { - ParsedArgs { - in_file: matches.value_of("FILE").unwrap().to_string(), - format: match matches.value_of("format") { - Some("xml") => PlistFormat::Xml, - Some("bin") | Some("binary") => PlistFormat::Binary, - _ => failure_and_exit!("Plist format error."), - }, - out_file: matches.value_of("out").map(ToString::to_string), - search: matches.value_of("search").map(ToString::to_string), - } -} - -pub fn get_args_matches() -> ArgMatches<'static> { - App::new(env!("CARGO_PKG_NAME")) - .version(env!("CARGO_PKG_VERSION")) - .author(env!("CARGO_PKG_AUTHORS")) - .about(env!("CARGO_PKG_DESCRIPTION")) - .arg(Arg::with_name("format").short("f").long("format").takes_value(true) - .default_value("xml") - .possible_values(&["xml", "binary", "bin"]) - .help("Output plist format") - ) - .arg(Arg::with_name("search").long("search").takes_value(true).help("Search plist value")) - .arg(Arg::with_name("out").short("o").long("out").takes_value(true).help("Output plist file")) - .arg(Arg::with_name("FILE").required(true).index(1).help("Input plist file name")) - .get_matches() -} \ No newline at end of file diff --git a/src/cmd.rs b/src/cmd.rs new file mode 100644 index 0000000..cf38046 --- /dev/null +++ b/src/cmd.rs @@ -0,0 +1,10 @@ +use clap::{ArgMatches, App}; +use rust_util::XResult; + +pub type CommandError = XResult<()>; + +pub trait Command { + fn name(&self) -> &str; + fn subcommand<'a>(&self) -> App<'a, 'a>; + fn run(&self, arg_matches: &ArgMatches, _: &ArgMatches) -> CommandError; +} \ No newline at end of file diff --git a/src/cmd_find.rs b/src/cmd_find.rs new file mode 100644 index 0000000..735452b --- /dev/null +++ b/src/cmd_find.rs @@ -0,0 +1,32 @@ +use clap::{ArgMatches, SubCommand, App, Arg}; +use crate::cmd::{Command, CommandError}; +use crate::find; +use plist::Value; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "find" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Find subcommand") + .arg(Arg::with_name("search").long("search").takes_value(true).required(true).help("Search plist value")) + .arg(Arg::with_name("FILE").required(true).index(1).help("Input plist file name")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let search = sub_arg_matches.value_of("search").unwrap(); + let in_file = sub_arg_matches.value_of("FILE").unwrap(); + // read plist file + let plist_value = Value::from_file(in_file).unwrap_or_else(|e| { + failure_and_exit!("Read plist file: {}, failed: {}", in_file, e); + }); + + let matched = find::find(&plist_value, &search); + for (k, v) in &matched { + information!("Matched: {} -> {}", k, v); + } + success!("Found {} values(s)", matched.len()); + Ok(()) + } +} \ No newline at end of file diff --git a/src/cmd_plist_default.rs b/src/cmd_plist_default.rs new file mode 100644 index 0000000..70bf0a0 --- /dev/null +++ b/src/cmd_plist_default.rs @@ -0,0 +1,82 @@ +use std::fs::File; +use clap::{App, Arg, ArgMatches}; +use plist::Value; +use crate::cmd::CommandError; +use crate::utils::print_bytes; + +pub enum PlistFormat { + Xml, + Binary, +} + +pub struct ParsedArgs { + pub in_file: String, + pub format: PlistFormat, + pub out_file: Option, +} + +pub struct CommandImpl; + +impl CommandImpl { + pub fn process_command<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app.arg(Arg::with_name("format").short("f").long("format").takes_value(true) + .default_value("xml") + .possible_values(&["xml", "binary", "bin"]) + .help("Output plist format") + ) + .arg(Arg::with_name("out").short("o").long("out").takes_value(true).help("Output plist file")) + .arg(Arg::with_name("FILE").required(true).index(1).help("Input plist file name")) + } + pub fn run(matches: &ArgMatches) -> CommandError { + let parsed_args = parse_args(matches); + + // read plist file + let plist_value = Value::from_file(&parsed_args.in_file).unwrap_or_else(|e| { + failure_and_exit!("Read plist file: {}, failed: {}", parsed_args.in_file, e); + }); + + // write plist to file or stdout + if let Some(out_file) = parsed_args.out_file { + if File::open(&out_file).is_ok() { + failure_and_exit!("Output file exits: {}", out_file); + } + match parsed_args.format { + PlistFormat::Xml => if let Err(e) = plist_value.to_file_xml(&out_file) { + failure_and_exit!("Write xml plist file failed: {}", e); + } else { + success!("Write xml plist file: {} success", out_file); + } + PlistFormat::Binary => if let Err(e) = plist_value.to_file_binary(&out_file) { + failure_and_exit!("Write binary plist file failed: {}", e); + } else { + success!("Write binary plist file: {} success", out_file); + } + } + } else { + let mut buf = vec![]; + match parsed_args.format { + PlistFormat::Xml => if let Err(e) = plist_value.to_writer_xml(&mut buf) { + failure_and_exit!("Write xml plist failed: {}", e); + } + PlistFormat::Binary => if let Err(e) = plist_value.to_writer_binary(&mut buf) { + failure_and_exit!("Write binary plist failed: {}", e); + } + } + print_bytes(&buf); + } + + Ok(()) + } +} + +fn parse_args(matches: &ArgMatches) -> ParsedArgs { + ParsedArgs { + in_file: matches.value_of("FILE").unwrap().to_string(), + format: match matches.value_of("format") { + Some("xml") => PlistFormat::Xml, + Some("bin") | Some("binary") => PlistFormat::Binary, + _ => failure_and_exit!("Plist format error."), + }, + out_file: matches.value_of("out").map(ToString::to_string), + } +} diff --git a/src/main.rs b/src/main.rs index 35f30a7..fe93c67 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,60 +1,32 @@ #[macro_use] extern crate rust_util; +mod cmd; mod utils; -mod args; mod find; +mod cmd_plist_default; +mod cmd_find; -use std::fs::File; -use plist::Value; -use crate::utils::print_bytes; -use crate::args::PlistFormat; +use clap::App; +use cmd::{Command, CommandError}; -fn main() { - let matches = args::get_args_matches(); - let parsed_args = args::parse_args(matches); +fn main() -> CommandError { + let commands: Vec> = vec![ + Box::new(cmd_find::CommandImpl) + ]; + let mut app = App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .about(env!("CARGO_PKG_DESCRIPTION")); - // read plist file - let plist_value = Value::from_file(&parsed_args.in_file).unwrap_or_else(|e| { - failure_and_exit!("Read plist file: {}, failed: {}", parsed_args.in_file, e); - }); - - if let Some(search) = parsed_args.search { - let matched = find::find(&plist_value, &search); - for (k, v) in &matched { - information!("Matched: {} -> {}", k, v); - } - success!("Found {} values(s)", matched.len()); - return; + app = cmd_plist_default::CommandImpl::process_command(app); + for command in &commands { + app = app.subcommand(command.subcommand()); } - - // write plist to file or stdout - if let Some(out_file) = parsed_args.out_file { - if File::open(&out_file).is_ok() { - failure_and_exit!("Output file exits: {}", out_file); + let matches = app.get_matches(); + for command in &commands { + if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) { + return command.run(&matches, sub_cmd_matches); } - match parsed_args.format { - PlistFormat::Xml => if let Err(e) = plist_value.to_file_xml(&out_file) { - failure_and_exit!("Write xml plist file failed: {}", e); - } else { - success!("Write xml plist file: {} success", out_file); - } - PlistFormat::Binary => if let Err(e) = plist_value.to_file_binary(&out_file) { - failure_and_exit!("Write binary plist file failed: {}", e); - } else { - success!("Write binary plist file: {} success", out_file); - } - } - } else { - let mut buf = vec![]; - match parsed_args.format { - PlistFormat::Xml => if let Err(e) = plist_value.to_writer_xml(&mut buf) { - failure_and_exit!("Write xml plist failed: {}", e); - } - PlistFormat::Binary => if let Err(e) = plist_value.to_writer_binary(&mut buf) { - failure_and_exit!("Write binary plist failed: {}", e); - } - } - print_bytes(&buf); } + cmd_plist_default::CommandImpl::run(&matches) }