From e496a8063dfa7e6695f4782ded4bfcebb50e41cd Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sat, 1 Nov 2025 22:25:05 +1100 Subject: [PATCH] Ensure OptIdentifier::Multi(names) can never be empty --- src/option.rs | 19 ++++++++++++++----- src/std.rs | 2 +- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/option.rs b/src/option.rs index fd4fa2c..adf1f6d 100644 --- a/src/option.rs +++ b/src/option.rs @@ -29,29 +29,38 @@ pub struct Opt { // 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, required: bool) -> 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, required } + } + /// 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 { id, names: OptIdentifier::Single(name), value_name: None, help_string, r#type: OptType::Positional, required: false } } /// 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 { id, names: OptIdentifier::Single(name), value_name: None, help_string, r#type: OptType::Positional, required: true } + Self::new(id, OptIdentifier::Single(name), None, help_string, OptType::Positional, true) } /// A flag-type option that takes no value pub const fn flag(id: ID, names: &'static[&'static str], help_string: &'static str) -> Self { - Self { id, names: OptIdentifier::Multi(names), value_name: None, help_string, r#type: OptType::Flag, required: false } + Self::new(id, OptIdentifier::Multi(names), None, help_string, OptType::Flag, false) } /// 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 { id, names: OptIdentifier::Multi(names), value_name: None, help_string, r#type: OptType::Flag, required: true } + Self::new(id, OptIdentifier::Multi(names), None, help_string, OptType::Flag, true) } /// 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 { id, names: OptIdentifier::Multi(names), value_name: Some(value_name), help_string, r#type: OptType::Value, required: false } + Self::new(id, OptIdentifier::Multi(names), Some(value_name), help_string, OptType::Value, false) } /// 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 { id, names: OptIdentifier::Multi(names), value_name: Some(value_name), help_string, r#type: OptType::Value, required: true } + Self::new(id, OptIdentifier::Multi(names), Some(value_name), help_string, OptType::Value, true) } } diff --git a/src/std.rs b/src/std.rs index 27915ef..16a0354 100644 --- a/src/std.rs +++ b/src/std.rs @@ -74,7 +74,7 @@ impl Opts<&'static str> { pub fn parse_map<'a, S: AsRef + 'a, I: Iterator>(&self, program_name: &str, args: I, error: impl FnOnce(&str, ParseError)) -> ParseMapResult { let mut out: BTreeMap<&'static str, String> = BTreeMap::new(); - match self.parse(&program_name, args, |program_name, id, _opt, _name, arg| { + match self.parse(&program_name, args, |_program_name, id, _opt, _name, arg| { out.insert(id, arg.into()); Ok(ParseControl::Continue) }, error) {