feat: use clap subcommand
This commit is contained in:
42
src/args.rs
42
src/args.rs
@@ -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<String>,
|
||||
pub search: Option<String>,
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
10
src/cmd.rs
Normal file
10
src/cmd.rs
Normal file
@@ -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;
|
||||
}
|
||||
32
src/cmd_find.rs
Normal file
32
src/cmd_find.rs
Normal file
@@ -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(())
|
||||
}
|
||||
}
|
||||
82
src/cmd_plist_default.rs
Normal file
82
src/cmd_plist_default.rs
Normal file
@@ -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<String>,
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
68
src/main.rs
68
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<Box<dyn Command>> = 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);
|
||||
app = cmd_plist_default::CommandImpl::process_command(app);
|
||||
for command in &commands {
|
||||
app = app.subcommand(command.subcommand());
|
||||
}
|
||||
success!("Found {} values(s)", matched.len());
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user