From 912d282d12de9a4f87082f90e448973eba66e068 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sun, 2 Nov 2025 01:16:17 +1100 Subject: [PATCH] Move help text and required flag to with chain, make help text optional --- examples/bin2h.rs | 10 +++++----- examples/btreemap.rs | 8 ++++---- src/help.rs | 18 ++++++++++------- src/option.rs | 47 +++++++++++++++++++++----------------------- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/examples/bin2h.rs b/examples/bin2h.rs index b2beb95..beefa34 100644 --- a/examples/bin2h.rs +++ b/examples/bin2h.rs @@ -203,11 +203,11 @@ pub fn main() -> ExitCode { // Read & parse arguments from the command line, store results into the above structure enum Arg { Out, Bin, Txt, Whitespace, Help } const OPTIONS: Opts = Opts::new(&[ - Opt::help_flag(Arg::Help, &["--help", "-h"], "Show this help message and exit"), - Opt::positional_required(Arg::Out, "out", "Path to generated header file"), - Opt::value(Arg::Bin, &["--bin", "-b"], "data.bin", "Add a binary file"), - Opt::value(Arg::Txt, &["--txt", "-t"], "text.txt", "Add a text file"), - Opt::value(Arg::Whitespace, &["--whitespace"], "\" \"", "Emitted indentation (Default: \"\\t\")"), + Opt::help_flag(Arg::Help, &["--help", "-h"]).help_text("Show this help message and exit"), + Opt::positional(Arg::Out, "out").help_text("Path to generated header file").required(), + Opt::value(Arg::Bin, &["--bin", "-b"], "data.bin").help_text("Add a binary file"), + Opt::value(Arg::Txt, &["--txt", "-t"], "text.txt").help_text("Add a text file"), + Opt::value(Arg::Whitespace, &["--whitespace"], "\" \"").help_text("Emitted indentation (Default: \"\\t\")"), ]); match OPTIONS.parse_easy(|program_name, id, _opt, _name, arg| { match id { diff --git a/examples/btreemap.rs b/examples/btreemap.rs index f8ff3d2..a08da4a 100644 --- a/examples/btreemap.rs +++ b/examples/btreemap.rs @@ -8,10 +8,10 @@ use std::process::ExitCode; fn main() -> ExitCode { const OPTIONS: Opts<&'static str> = Opts::new(&[ - Opt::help_flag("help", &["--help"], "Show this help"), - Opt::positional("positional", "positional", "Positional argument"), - Opt::value("value", &["-v", "--value"], "string", "Value option"), - Opt::flag("flag", &["-f", "--flag"], "Flag option"), + Opt::help_flag("help", &["--help"]).help_text("Show this help"), + Opt::positional("positional", "positional").help_text("Positional argument"), + Opt::value("value", &["-v", "--value"], "string").help_text("Value option"), + Opt::flag("flag", &["-f", "--flag"]).help_text("Flag option"), ]); let map = match OPTIONS.parse_map_easy() { diff --git a/src/help.rs b/src/help.rs index 82bd2fd..cfd8073 100644 --- a/src/help.rs +++ b/src/help.rs @@ -134,10 +134,12 @@ impl core::fmt::Display for StandardFullHelpWriter<'_, ID> { } // Write positional argument line - writeln!(f, " {name} {:. core::fmt::Display for StandardFullHelpWriter<'_, ID> { } // Write padding and help text - writeln!(f, " {:. { id: ID, names: OptIdentifier, value_name: Option<&'static str>, - help_string: &'static str, + help_string: Option<&'static str>, r#type: OptType, flags: OptFlag, } @@ -40,50 +40,47 @@ impl OptFlag { // TODO: Improve this interface by making the name field take AsOptIdentifier when const traits are stabilised impl Opt { #[inline] - const fn new(id: ID, names: OptIdentifier, value_name: Option<&'static str>, help_string: &'static str, r#type: OptType) -> Self { + const fn new(id: ID, names: OptIdentifier, value_name: Option<&'static str>, r#type: OptType) -> Self { assert!(match names { OptIdentifier::Single(_) => true, OptIdentifier::Multi(names) => !names.is_empty(), }, "Option names cannot be an empty slice"); - Self { id, names, value_name, help_string, r#type, flags: OptFlag::NONE } + Self { id, names, value_name, help_string: None, r#type, flags: OptFlag::NONE } } /// A positional argument that is parsed sequentially without being invoked by an option flag - pub const fn positional(id: ID, name: &'static str, help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Single(name), None, help_string, OptType::Positional) - } - /// A required positional argument that is parsed sequentially without being invoked by an option flag - pub const fn positional_required(id: ID, name: &'static str, help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Single(name), None, help_string, OptType::Positional).with_required() + pub const fn positional(id: ID, name: &'static str) -> Self { + Self::new(id, OptIdentifier::Single(name), None, OptType::Positional) } /// A flag-type option that serves as the interface's help flag - pub const fn help_flag(id: ID, names: &'static[&'static str], help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Multi(names), None, help_string, OptType::Flag).with_help_flag() + pub const fn help_flag(id: ID, names: &'static[&'static str]) -> Self { + Self::new(id, OptIdentifier::Multi(names), None, OptType::Flag) + .with_help_flag() } - /// A flag-type option that takes no value - pub const fn flag(id: ID, names: &'static[&'static str], help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Multi(names), None, help_string, OptType::Flag) - } - /// A required flag-type option that takes no value - pub const fn flag_required(id: ID, names: &'static[&'static str], help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Multi(names), None, help_string, OptType::Flag).with_required() + /// A flag-type option, takes no value + pub const fn flag(id: ID, names: &'static[&'static str]) -> Self { + Self::new(id, OptIdentifier::Multi(names), None, OptType::Flag) } /// An option argument that takes a value - pub const fn value(id: ID, names: &'static[&'static str], value_name: &'static str, help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Multi(names), Some(value_name), help_string, OptType::Value) - } - /// A required option argument that takes a value - pub const fn value_required(id: ID, names: &'static[&'static str], value_name: &'static str, help_string: &'static str) -> Self { - Self::new(id, OptIdentifier::Multi(names), Some(value_name), help_string, OptType::Value).with_required() + pub const fn value(id: ID, names: &'static[&'static str], value_name: &'static str) -> Self { + Self::new(id, OptIdentifier::Multi(names), Some(value_name), OptType::Value) } + /// This option is required, ie; parsing will fail if it is not specified. #[inline] - const fn with_required(mut self) -> Self { + pub const fn required(mut self) -> Self { assert!(!self.is_help(), "Help flag cannot be made required"); self.flags.0 |= OptFlag::REQUIRED.0; self } + /// Sets the help string for an option. + #[inline] + pub const fn help_text(mut self, help_string: &'static str) -> Self { + self.help_string = Some(help_string); + self + } + #[inline] const fn with_help_flag(mut self) -> Self { assert!(matches!(self.r#type, OptType::Flag), "Only flags are allowed to be help options");