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]
|
#[macro_use]
|
||||||
extern crate rust_util;
|
extern crate rust_util;
|
||||||
|
|
||||||
|
mod cmd;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod args;
|
|
||||||
mod find;
|
mod find;
|
||||||
|
mod cmd_plist_default;
|
||||||
|
mod cmd_find;
|
||||||
|
|
||||||
use std::fs::File;
|
use clap::App;
|
||||||
use plist::Value;
|
use cmd::{Command, CommandError};
|
||||||
use crate::utils::print_bytes;
|
|
||||||
use crate::args::PlistFormat;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() -> CommandError {
|
||||||
let matches = args::get_args_matches();
|
let commands: Vec<Box<dyn Command>> = vec![
|
||||||
let parsed_args = args::parse_args(matches);
|
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
|
app = cmd_plist_default::CommandImpl::process_command(app);
|
||||||
let plist_value = Value::from_file(&parsed_args.in_file).unwrap_or_else(|e| {
|
for command in &commands {
|
||||||
failure_and_exit!("Read plist file: {}, failed: {}", parsed_args.in_file, e);
|
app = app.subcommand(command.subcommand());
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
let matches = app.get_matches();
|
||||||
// write plist to file or stdout
|
for command in &commands {
|
||||||
if let Some(out_file) = parsed_args.out_file {
|
if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) {
|
||||||
if File::open(&out_file).is_ok() {
|
return command.run(&matches, sub_cmd_matches);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
cmd_plist_default::CommandImpl::run(&matches)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user