use crate::{ context::Context, opt::{self, Opt}, vox, App, }; pub type Handler = fn(&App, &Context, &[String]); #[derive(Clone)] pub struct Command { pub name: String, pub commands: Vec, pub handler: Option, pub opts: Vec, pub long_desc: Option, pub short_desc: Option, } impl Command { pub fn new>(name: T) -> Self { Command { name: name.into(), commands: vec![], handler: None, opts: vec![], long_desc: None, short_desc: None, } } pub fn handler(mut self, f: Handler) -> Self { self.handler = Some(f); self } pub fn opt(mut self, opt: Opt) -> Self { self.opts.push(opt); self } pub fn short_desc(mut self, short_desc: &str) -> Self { self.short_desc = Some(short_desc.into()); self } pub fn long_desc(mut self, long_desc: &str) -> Self { self.short_desc = Some(long_desc.into()); self } } pub(crate) fn help(app: &App, _ctx: &Context, args: &[String]) { vox::print(app.application_header()); print_command_help(&app.root, args); } pub(crate) fn print_command_help(cmd: &Command, args: &[String]) { if !args.is_empty() { if let Some(cmd) = cmd.commands.iter().find(|c| Some(&c.name) == args.first()) { return print_command_help(cmd, &args[1..]); } } vox::print(""); if let Some(desc) = cmd.long_desc.as_ref().or(cmd.short_desc.as_ref()) { if cmd.name != "root" { vox::header(&cmd.name.to_uppercase()); } vox::print(desc); vox::print(""); } if !cmd.opts.is_empty() { vox::header("OPTIONS"); vox::description_list( cmd.opts .iter() .map(|o| (o.usage(), o.desc.clone().unwrap_or_default())) .collect(), ) } if !cmd.commands.is_empty() { vox::header("Subcommands"); vox::description_list( cmd.commands .iter() .map(|c| (c.name.clone(), c.short_desc.clone().unwrap_or_default())) .collect(), ) } }