From 19d967ef0eb7887c5c00fdbcf95bee694d16749b Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 3 Jan 2021 01:19:34 +0800 Subject: [PATCH] feat: add use clap --- .gitignore | 2 +- demo/test_clap/Cargo.toml | 11 ++++++ demo/test_clap/src/main.rs | 24 +++++++++++ src/util_clap.rs | 81 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 demo/test_clap/Cargo.toml create mode 100644 demo/test_clap/src/main.rs create mode 100644 src/util_clap.rs diff --git a/.gitignore b/.gitignore index 5feb64e..8780e93 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Generated by Cargo # will have compiled files and executables -/target/ +target/ Cargo.lock # These are backup files generated by rustfmt diff --git a/demo/test_clap/Cargo.toml b/demo/test_clap/Cargo.toml new file mode 100644 index 0000000..45a68a2 --- /dev/null +++ b/demo/test_clap/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test_clap" +version = "0.1.0" +authors = ["Hatter Jiang "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rust_util = { version = "0.6", path = "../../", features = ["use_clap"] } +clap = "2.0" diff --git a/demo/test_clap/src/main.rs b/demo/test_clap/src/main.rs new file mode 100644 index 0000000..8bf0c50 --- /dev/null +++ b/demo/test_clap/src/main.rs @@ -0,0 +1,24 @@ +use clap::{App, SubCommand, ArgMatches}; +use rust_util::util_clap::Command; +use rust_util::util_clap::CommandError; +use rust_util::util_clap::CommandExecutor; + +struct TestCommand{} +impl Command for TestCommand { + fn name(&self) -> &str { "test" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("Test subcommand") + } + + fn run(&self, _arg_matches: &ArgMatches, _: &ArgMatches) -> CommandError { + println!("hello test!"); + Ok(None) + } +} + +fn main() { + let mut c = CommandExecutor::new(None); + c.add(Box::new(TestCommand{})); + c.run().unwrap(); +} diff --git a/src/util_clap.rs b/src/util_clap.rs new file mode 100644 index 0000000..ff048e9 --- /dev/null +++ b/src/util_clap.rs @@ -0,0 +1,81 @@ +use std::process; +use clap::{App, Arg, ArgMatches}; +use crate::XResult; +use crate::util_msg; + +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; +} + +pub trait DefaultCommand { + fn process_command<'a>(&self, app: App<'a, 'a>) -> App<'a, 'a>; + fn run(&self, arg_matches: &ArgMatches) -> CommandError; +} + +pub struct DefaultCommandImpl {} +impl DefaultCommand for DefaultCommandImpl { + fn process_command<'a>(&self, app: App<'a, 'a>) -> App<'a, 'a> { + app.arg(Arg::with_name("verbose").long("verbose").short("v").multiple(true).help("Show verbose info")) + } + + fn run(&self, arg_matches: &ArgMatches) -> CommandError { + let verbose_count = arg_matches.occurrences_of("verbose"); + util_msg::print_info(&format!("Verbose count: {}", verbose_count)); + util_msg::print_info("This is default command cli, please run with help (--help)"); + Ok(None) + } +} + +pub struct CommandExecutor { + default_cmd: Option>, + commands: Vec>, +} + +impl CommandExecutor { + pub fn new(default_cmd: Option>) -> Self { + CommandExecutor{ + default_cmd, + commands: Vec::new(), + } + } + + pub fn add(&mut self, cmd: Box) -> &mut Self { + self.commands.push(cmd); + self + } + + pub fn run(&self) -> CommandError { + let mut app = App::new(env!("CARGO_PKG_NAME")) + .version(env!("CARGO_PKG_VERSION")) + .about(env!("CARGO_PKG_DESCRIPTION")); + if let Some(default_cmd) = &self.default_cmd { + app = default_cmd.process_command(app); + } + for command in &self.commands { + app = app.subcommand(command.subcommand()); + } + let matches = app.get_matches(); + for command in &self.commands { + if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) { + match command.run(&matches, sub_cmd_matches)? { + None => return Ok(None), + Some(code) => process::exit(code), + } + } + } + match &self.default_cmd { + None => { + util_msg::print_error("No default command, please try help (--help)"); + process::exit(1); + }, + Some(default_cmd) => match default_cmd.run(&matches)? { + None => return Ok(None), + Some(code) => process::exit(code), + }, + } + } +}