use std::convert::AsRef;
#[deprecated(since = "1.2.0", note = "Use either `OptionSet` or `OptionSetEx` now, as applicable")]
pub type Options<'a> = OptionSetEx<'a>;
pub(crate) const ABBR_SUP_DEFAULT: bool = true;
pub(crate) const MODE_DEFAULT: OptionsMode = OptionsMode::Standard;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptionSetEx<'a> {
pub long: Vec<LongOption<'a>>,
pub short: Vec<ShortOption>,
pub mode: OptionsMode,
pub allow_abbreviations: bool,
}
impl<'a> Default for OptionSetEx<'a> {
fn default() -> Self {
OptionSetEx::new(0, 0)
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct OptionSet<'r, 'a: 'r> {
pub long: &'r [LongOption<'a>],
pub short: &'r [ShortOption],
pub mode: OptionsMode,
pub allow_abbreviations: bool,
}
impl<'r, 'a: 'r> PartialEq<OptionSet<'r, 'a>> for OptionSetEx<'a> {
fn eq(&self, rhs: &OptionSet<'r, 'a>) -> bool {
rhs.eq(&self.as_fixed())
}
}
impl<'r, 'a: 'r> PartialEq<OptionSetEx<'a>> for OptionSet<'r, 'a> {
fn eq(&self, rhs: &OptionSetEx<'a>) -> bool {
self.eq(&rhs.as_fixed())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptionsMode {
Standard,
Alternate,
}
impl Default for OptionsMode {
fn default() -> Self {
MODE_DEFAULT
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LongOption<'a> {
pub name: &'a str,
pub expects_data: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShortOption {
pub ch: char,
pub expects_data: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub enum OptionFlaw<'a> {
LongEmpty,
LongIncludesEquals(&'a str),
ShortDash,
ShortDup(char),
LongDup(&'a str),
}
impl<'a> OptionSetEx<'a> {
pub fn new(count_long: usize, count_short: usize) -> Self {
Self {
long: Vec::with_capacity(count_long),
short: Vec::with_capacity(count_short),
mode: MODE_DEFAULT,
allow_abbreviations: ABBR_SUP_DEFAULT,
}
}
pub fn as_fixed(&self) -> OptionSet<'_, 'a> {
OptionSet {
long: &self.long[..],
short: &self.short[..],
mode: self.mode,
allow_abbreviations: self.allow_abbreviations,
}
}
pub fn set_mode(&mut self, mode: OptionsMode) -> &mut Self {
self.mode = mode;
self
}
pub fn set_allow_abbreviations(&mut self, allow: bool) -> &mut Self {
self.allow_abbreviations = allow;
self
}
pub fn is_empty(&self) -> bool {
self.long.is_empty() && self.short.is_empty()
}
pub fn add_long(&mut self, name: &'a str) -> &mut Self {
self.long.push(LongOption::new(name, false));
self
}
pub fn add_short(&mut self, ch: char) -> &mut Self {
self.short.push(ShortOption::new(ch, false));
self
}
pub fn add_long_data(&mut self, name: &'a str) -> &mut Self {
self.long.push(LongOption::new(name, true));
self
}
pub fn add_short_data(&mut self, ch: char) -> &mut Self {
self.short.push(ShortOption::new(ch, true));
self
}
pub fn add_existing_long(&mut self, long: LongOption<'a>) -> &mut Self {
self.long.push(long);
self
}
pub fn add_existing_short(&mut self, short: ShortOption) -> &mut Self {
self.short.push(short);
self
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
validation::validate_set(&self.as_fixed(), false).is_ok()
}
#[inline(always)]
pub fn validate(&self) -> Result<(), Vec<OptionFlaw<'a>>> {
validation::validate_set(&self.as_fixed(), true)
}
pub fn process<T>(&self, args: &'a [T]) -> super::analysis::Analysis<'a>
where T: AsRef<str>
{
super::engine::process(args, &self.as_fixed())
}
}
impl<'r, 'a: 'r> OptionSet<'r, 'a> {
pub fn to_extendible(&self) -> OptionSetEx<'a> {
OptionSetEx {
long: self.long.iter().cloned().collect(),
short: self.short.iter().cloned().collect(),
mode: self.mode,
allow_abbreviations: self.allow_abbreviations,
}
}
pub fn set_mode(&mut self, mode: OptionsMode) -> &mut Self {
self.mode = mode;
self
}
pub fn set_allow_abbreviations(&mut self, allow: bool) -> &mut Self {
self.allow_abbreviations = allow;
self
}
pub fn is_empty(&self) -> bool {
self.long.is_empty() && self.short.is_empty()
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
validation::validate_set(self, false).is_ok()
}
#[inline(always)]
pub fn validate(&'r self) -> Result<(), Vec<OptionFlaw<'a>>> {
validation::validate_set(self, true)
}
pub fn process<T>(&self, args: &'a [T]) -> super::analysis::Analysis<'a>
where T: AsRef<str>
{
super::engine::process(args, self)
}
}
impl<'a> LongOption<'a> {
fn new(name: &'a str, expects_data: bool) -> Self {
debug_assert!(!name.is_empty(), "Long option name cannot be an empty string!");
debug_assert!(!name.contains('='), "Long option name cannot contain ‘=’!");
Self { name, expects_data, }
}
}
impl ShortOption {
fn new(ch: char, expects_data: bool) -> Self {
debug_assert_ne!('-', ch, "Dash (‘-’) is not a valid short option!");
Self { ch, expects_data, }
}
}
mod validation {
use super::{OptionSet, OptionFlaw};
pub fn validate_set<'r, 'a: 'r>(set: &OptionSet<'r, 'a>, detail: bool
) -> Result<(), Vec<OptionFlaw<'a>>>
{
let mut flaws = Vec::new();
for candidate in set.long {
if candidate.name.is_empty() {
match detail {
true => { flaws.push(OptionFlaw::LongEmpty); },
false => { return Err(flaws); },
}
}
else if candidate.name.contains('=') {
match detail {
true => { flaws.push(OptionFlaw::LongIncludesEquals(candidate.name)); },
false => { return Err(flaws); },
}
}
}
for candidate in set.short {
if candidate.ch == '-' {
match detail {
true => { flaws.push(OptionFlaw::ShortDash); },
false => { return Err(flaws); },
}
}
}
let mut dupes: bool = false;
find_duplicates_short(set, &mut flaws, detail, &mut dupes);
if !detail && dupes {
return Err(flaws);
}
find_duplicates_long(set, &mut flaws, detail, &mut dupes);
if !detail && dupes {
return Err(flaws);
}
match flaws.is_empty() {
true => Ok(()),
false => Err(flaws),
}
}
fn find_duplicates_short<'r, 'a: 'r>(set: &OptionSet<'r, 'a>, flaws: &mut Vec<OptionFlaw<'a>>,
detail: bool, found: &mut bool)
{
let opts = set.short;
let mut checked: Vec<char> = Vec::with_capacity(opts.len());
let mut duplicates = Vec::new();
for short in opts {
let ch = short.ch;
if !duplicates.contains(&OptionFlaw::ShortDup(ch)) {
match checked.contains(&ch) {
true => {
match detail {
true => { duplicates.push(OptionFlaw::ShortDup(ch)); },
false => { *found = true; return; },
}
},
false => { checked.push(ch); },
}
}
}
if !duplicates.is_empty() {
flaws.append(&mut duplicates);
}
}
fn find_duplicates_long<'r, 'a: 'r>(set: &OptionSet<'r, 'a>, flaws: &mut Vec<OptionFlaw<'a>>,
detail: bool, found: &mut bool)
{
let opts = set.long;
let mut checked: Vec<&'a str> = Vec::with_capacity(opts.len());
let mut duplicates = Vec::new();
for long in opts {
let name = long.name.clone();
if !duplicates.contains(&OptionFlaw::LongDup(name)) {
match checked.contains(&name) {
true => {
match detail {
true => { duplicates.push(OptionFlaw::LongDup(name)); },
false => { *found = true; return; },
}
},
false => { checked.push(name); },
}
}
}
if !duplicates.is_empty() {
flaws.append(&mut duplicates);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn create_short_dash() {
let _opt = ShortOption::new('-', false); }
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn create_long_no_name() {
let _opt = LongOption::new("", false); }
#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn create_long_with_equals() {
let _opt = LongOption::new("a=b", false); }
}