meap 0.3.0

Minimal Extensible Argument Parser
Documentation
use crate::low_level;
use std::env;
use std::error;
use std::fmt;
use std::process;
use std::str::FromStr;

#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub enum Name {
    Long(String),
    Short(char),
}

impl Name {
    fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for Name {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            Self::Long(long) => write!(f, "--{}", long),
            Self::Short(short) => write!(f, "-{}", short),
        }
    }
}

pub trait IntoName {
    fn into_name(self) -> Name;
}

impl IntoName for Name {
    fn into_name(self) -> Name {
        self
    }
}

impl IntoName for char {
    fn into_name(self) -> Name {
        Name::Short(self)
    }
}

impl<'a> IntoName for &'a str {
    fn into_name(self) -> Name {
        Name::Long(self.to_string())
    }
}

impl IntoName for String {
    fn into_name(self) -> Name {
        Name::Long(self)
    }
}

#[derive(Debug)]
pub enum ParseError {
    UnhandledPositionalArguments(Vec<String>),
    UnknownName(Name),
    ArgumentLacksParameter(Name),
    UnexpectedArgumentParam { name: Name, value: String },
    MissingRequiredPositionalArgument(String),
    MissingRequiredArgument(Name),
    ExpectedOneArgument(Name),
    UnableToParsePositionalArgumentParam { hint: String, value: String },
    UnableToParseArgumentParam { name: Name, value: String },
    MultipleMutuallyExclusiveOptionsChosen,
    MissingRequiredArgumentGeneral(String),
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            Self::UnhandledPositionalArguments(values) => {
                write!(f, "Unhandled positional arguments: {}", values.join(", "))
            }
            Self::UnknownName(name) => write!(f, "Argument \"{}\" does not exist", name),
            Self::ArgumentLacksParameter(name) => {
                write!(f, "Argument \"{}\" lacks parameter", name)
            }
            Self::UnexpectedArgumentParam { name, value } => {
                write!(f, "Unexpected param \"{}\" to argument \"{}\"", value, name)
            }
            Self::MissingRequiredPositionalArgument(hint) => {
                write!(f, "Required positional argument \"{}\" is missing", hint)
            }
            Self::MissingRequiredArgument(name) => {
                write!(f, "Required argument \"{}\" is missing", name)
            }
            Self::ExpectedOneArgument(name) => write!(
                f,
                "Argument \"{}\" was passed multiple times but expected at most once",
                name
            ),
            Self::UnableToParsePositionalArgumentParam { hint, value } => write!(
                f,
                "Unable to parse \"{}\" given for positional argument \"{}\"",
                value, hint
            ),
            Self::UnableToParseArgumentParam { name, value } => write!(
                f,
                "Unable to parse value \"{}\" given for argument \"{}\"",
                value, name
            ),
            Self::MultipleMutuallyExclusiveOptionsChosen => {
                write!(f, "Multiple mutually-exclusive options chosen")
            }
            Self::MissingRequiredArgumentGeneral(error) => write!(f, "{}", error),
        }
    }
}

impl error::Error for ParseError {}

#[derive(Debug)]
pub enum SpecError {
    NameUsedMultipleTimes(Name),
}

impl fmt::Display for SpecError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            Self::NameUsedMultipleTimes(name) => {
                write!(f, "Name used multiple times: {}", name.to_string())
            }
        }
    }
}

impl error::Error for SpecError {}

/// A parser whose one-time-use state has been used up, and is only good for generating help
/// messages.
pub struct SpentParser<P: Parser> {
    parser: P,
    program_name: String,
}

impl<P: Parser> SpentParser<P> {
    pub fn into_help(self) -> Help {
        let mut help = Help::new(self.program_name);
        self.parser.update_help(&mut help);
        help
    }
}

pub trait Parser: Sized {
    type Item;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError>;

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>>;

    fn update_help(&self, help: &mut Help);

    fn parse_args<A: IntoIterator<Item = String>>(
        mut self,
        program_name: String,
        args: A,
    ) -> Result<Self::Item, (Box<dyn error::Error>, SpentParser<Self>)> {
        let mut low_level_parser = low_level::LowLevelParser::new(program_name.clone());
        if let Err(e) = self.register_low_level(&mut low_level_parser) {
            panic!("{}", e);
        }
        let mut low_level_output = match low_level_parser.parse(args) {
            Ok(low_level_output) => low_level_output,
            Err(parse_error) => {
                return Err((
                    parse_error,
                    SpentParser {
                        parser: self,
                        program_name,
                    },
                ))
            }
        };
        let item = match self.parse_low_level(&mut low_level_output) {
            Ok(item) => item,
            Err(parse_error) => {
                return Err((
                    parse_error,
                    SpentParser {
                        parser: self,
                        program_name,
                    },
                ))
            }
        };
        let unhandled_positional_arguments = low_level_output.free_iter().collect::<Vec<_>>();
        if !unhandled_positional_arguments.is_empty() {
            return Err((
                ParseError::UnhandledPositionalArguments(unhandled_positional_arguments).into(),
                SpentParser {
                    parser: self,
                    program_name,
                },
            ));
        }
        Ok(item)
    }

    fn parse_env(self) -> Result<Self::Item, (Box<dyn error::Error>, SpentParser<Self>)> {
        let mut env = env::args();
        let program_name = env.next().expect("no args");
        self.parse_args(program_name, env)
    }

    fn both<PU: Parser>(self, other: PU) -> Both<Self::Item, PU::Item, Self, PU> {
        Both {
            parser_t: self,
            parser_u: other,
        }
    }

    fn map<U, F: FnOnce(Self::Item) -> U>(self, f: F) -> Map<Self::Item, U, F, Self> {
        Map {
            f: Some(f),
            parser_t: self,
        }
    }

    fn with_help<N: IntoName>(self, name: N) -> WithHelp<Self::Item, Self> {
        WithHelp::new(self, name)
    }

    fn with_help_default(self) -> WithHelp<Self::Item, Self> {
        WithHelp::new_default(self)
    }

    fn with_general_default(self, value: Self::Item) -> WithGeneralDefault<Self::Item, Self> {
        WithGeneralDefault {
            value: Some(value),
            parser: self,
        }
    }

    fn with_general_default_lazy<F: FnOnce() -> Self::Item>(
        self,
        f: F,
    ) -> WithGeneralDefaultLazy<Self::Item, F, Self> {
        WithGeneralDefaultLazy {
            value: Some(f),
            parser: self,
        }
    }

    fn required<S: AsRef<str>>(self, error_message: S) -> GeneralRequired<Self> {
        GeneralRequired {
            parser: self,
            error_message: error_message.as_ref().to_string(),
        }
    }
}

/// Determines how many times the opt can be passed
pub trait Arity {
    fn arity_enum(&self) -> ArityEnum;
}

#[derive(Debug, Clone, Copy)]
pub enum ArityEnum {
    Required,
    Optional,
    Multiple,
}

pub mod arity {
    use super::{Arity, ArityEnum};

    pub struct Required;
    pub struct Optional;
    pub struct Multiple;

    impl Arity for Required {
        fn arity_enum(&self) -> ArityEnum {
            ArityEnum::Required
        }
    }
    impl Arity for Optional {
        fn arity_enum(&self) -> ArityEnum {
            ArityEnum::Optional
        }
    }
    impl Arity for Multiple {
        fn arity_enum(&self) -> ArityEnum {
            ArityEnum::Multiple
        }
    }
}

/// Determines whether the opt takes a value as an argument
pub trait HasParam {
    fn low_level() -> low_level::HasParam;
    fn maybe_hint(&self) -> Option<&str>;
}

pub mod has_param {
    use super::{low_level, HasParam};
    use std::marker::PhantomData;
    use std::str::FromStr;

    pub struct YesVia<V: FromStr, T: From<V>> {
        hint: String,
        phantom: PhantomData<(V, T)>,
    }
    pub struct No;

    impl<V: FromStr, T: From<V>> HasParam for YesVia<V, T> {
        fn low_level() -> low_level::HasParam {
            low_level::HasParam::Yes
        }
        fn maybe_hint(&self) -> Option<&str> {
            Some(self.hint.as_str())
        }
    }
    impl HasParam for No {
        fn low_level() -> low_level::HasParam {
            low_level::HasParam::No
        }
        fn maybe_hint(&self) -> Option<&str> {
            None
        }
    }

    impl<V: FromStr, T: From<V>> YesVia<V, T> {
        pub fn new<H: AsRef<str>>(hint: H) -> Self {
            Self {
                hint: hint.as_ref().to_string(),
                phantom: PhantomData,
            }
        }
        pub fn hint(&self) -> &str {
            self.hint.as_str()
        }
    }
}

pub trait NameType {
    fn names_to_register(&self) -> Option<&[Name]>;
}

pub mod name_type {
    use super::{IntoName, Name, NameType};

    #[derive(Debug, Clone)]
    pub struct Named {
        names: Vec<Name>,
    }
    pub struct Positional;

    impl NameType for Named {
        fn names_to_register(&self) -> Option<&[Name]> {
            Some(&self.names)
        }
    }
    impl NameType for Positional {
        fn names_to_register(&self) -> Option<&[Name]> {
            None
        }
    }

    impl Named {
        pub fn new<N: IntoName>(name: N) -> Self {
            Self {
                names: vec![name.into_name()],
            }
        }
        pub fn add<N: IntoName>(&mut self, name: N) {
            self.names.push(name.into_name());
        }
        pub fn names(&self) -> &[Name] {
            &self.names
        }
        pub fn first_name(&self) -> &Name {
            &self.names[0]
        }
    }
}

pub struct Arg<A: Arity, H: HasParam, N: NameType> {
    description: Option<String>,
    name_type: N,
    has_param: H,
    arity: A,
}

impl<A: Arity, H: HasParam, N: NameType> Arg<A, H, N> {
    pub fn new(arity: A, has_param: H, name_type: N) -> Self {
        Arg {
            description: None,
            name_type,
            has_param,
            arity,
        }
    }

    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.description = Some(description.as_ref().to_string());
        self
    }
}

impl<A: Arity, H: HasParam> Arg<A, H, name_type::Named> {
    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.name_type.add(name.into_name());
        self
    }
}

impl Arg<arity::Optional, has_param::No, name_type::Named> {
    pub fn some_if<T>(self, value: T) -> SomeIf<T> {
        SomeIf {
            value: Some(value),
            arg: self,
        }
    }
}

pub trait SingleArgParser {
    type SingleArgItem;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>>;
}

pub trait SingleArgParserHelp {
    fn help_message(&self) -> ArgHelp;
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Required, has_param::YesVia<V, T>, name_type::Positional>
{
    type SingleArgItem = T;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        let value = ll.free_iter().next().ok_or_else(|| {
            ParseError::MissingRequiredPositionalArgument(self.has_param.hint().to_string())
        })?;
        Ok(T::from(value.parse().map_err(|_| {
            ParseError::UnableToParsePositionalArgumentParam {
                hint: self.has_param.hint().to_string(),
                value,
            }
        })?))
    }
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Positional>
{
    type SingleArgItem = Option<T>;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        if let Some(value) = ll.free_iter().next() {
            Ok(Some(T::from(value.parse().map_err(|_| {
                ParseError::UnableToParsePositionalArgumentParam {
                    hint: self.has_param.hint().to_string(),
                    value,
                }
            })?)))
        } else {
            Ok(None)
        }
    }
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Multiple, has_param::YesVia<V, T>, name_type::Positional>
{
    type SingleArgItem = Vec<T>;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        let mut ret = Vec::new();
        for value in ll.free_iter() {
            ret.push(T::from(value.parse().map_err(|_| {
                ParseError::UnableToParsePositionalArgumentParam {
                    hint: self.has_param.hint().to_string(),
                    value,
                }
            })?));
        }
        Ok(ret)
    }
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Required, has_param::YesVia<V, T>, name_type::Named>
{
    type SingleArgItem = T;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        let values = ll.get_opt_values(self.name_type.names());
        match values.len() {
            0 => {
                Err(ParseError::MissingRequiredArgument(self.name_type.first_name().clone()).into())
            }
            1 => Ok(T::from(values[0].parse().map_err(|_| {
                ParseError::UnableToParseArgumentParam {
                    name: self.name_type.first_name().clone(),
                    value: values[0].clone(),
                }
            })?)),
            _ => Err(ParseError::ExpectedOneArgument(self.name_type.first_name().clone()).into()),
        }
    }
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>
{
    type SingleArgItem = Option<T>;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        let values = ll.get_opt_values(self.name_type.names());
        match values.len() {
            0 => Ok(None),
            1 => Ok(Some(T::from(values[0].parse().map_err(|_| {
                ParseError::UnableToParseArgumentParam {
                    name: self.name_type.first_name().clone(),
                    value: values[0].clone(),
                }
            })?))),
            _ => Err(ParseError::ExpectedOneArgument(self.name_type.first_name().clone()).into()),
        }
    }
}

impl<V: FromStr, T: From<V>> SingleArgParser
    for Arg<arity::Multiple, has_param::YesVia<V, T>, name_type::Named>
{
    type SingleArgItem = Vec<T>;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        let values = ll.get_opt_values(self.name_type.names());
        let mut ret = Vec::with_capacity(values.len());
        for v in values {
            ret.push(T::from(v.parse().map_err(|_| {
                ParseError::UnableToParseArgumentParam {
                    name: self.name_type.first_name().clone(),
                    value: v.clone(),
                }
            })?));
        }
        Ok(ret)
    }
}

impl SingleArgParser for Arg<arity::Optional, has_param::No, name_type::Named> {
    type SingleArgItem = bool;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        match ll.get_flag_count(self.name_type.names()) {
            0 => Ok(false),
            1 => Ok(true),
            _ => Err(ParseError::ExpectedOneArgument(self.name_type.first_name().clone()).into()),
        }
    }
}

impl SingleArgParser for Arg<arity::Multiple, has_param::No, name_type::Named> {
    type SingleArgItem = usize;

    fn parse_single_arg(
        &self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::SingleArgItem, Box<dyn error::Error>> {
        Ok(ll.get_flag_count(self.name_type.names()))
    }
}

impl<A: Arity, H: HasParam> SingleArgParserHelp for Arg<A, H, name_type::Named> {
    fn help_message(&self) -> ArgHelp {
        ArgHelp::Named(ArgHelpNamed {
            names: self.name_type.clone(),
            hint: self.has_param.maybe_hint().map(|h| h.to_string()),
            description: self.description.clone(),
            arity: self.arity.arity_enum(),
        })
    }
}

impl<V: FromStr, T: From<V>, A: Arity> SingleArgParserHelp
    for Arg<A, has_param::YesVia<V, T>, name_type::Positional>
{
    fn help_message(&self) -> ArgHelp {
        ArgHelp::Positional(ArgHelpPositional {
            hint: self.has_param.hint().to_string(),
            description: self.description.clone(),
            arity: self.arity.arity_enum(),
        })
    }
}

impl<V: FromStr, T: fmt::Display + From<V>>
    Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>
{
    pub fn with_default(self, value: T) -> WithDefaultDisplay<V, T> {
        WithDefaultDisplay {
            value_or_description: ValueOrDescription::Value(value),
            arg: self,
        }
    }
}

impl<V: FromStr, T: From<V>> Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named> {
    pub fn with_default_lazy<F: FnOnce() -> T, D: AsRef<str>>(
        self,
        description: D,
        thunk: F,
    ) -> WithDefaultLazy<V, T, F> {
        WithDefaultLazy {
            thunk: Some(thunk),
            arg: self,
            description: description.as_ref().to_string(),
        }
    }

    pub fn with_default_desc<D: AsRef<str>>(
        self,
        description: D,
        value: T,
    ) -> WithDefaultDescribed<V, T> {
        WithDefaultDescribed {
            value: Some(value),
            arg: self,
            description: description.as_ref().to_string(),
        }
    }

    pub fn with_default_parse<S: AsRef<str>>(self, value: S) -> WithDefaultParse<V, T> {
        WithDefaultParse {
            value: value.as_ref().to_string(),
            arg: self,
        }
    }
}

impl<A: Arity, H: HasParam, N: NameType> Parser for Arg<A, H, N>
where
    Self: SingleArgParser + SingleArgParserHelp,
{
    type Item = <Self as SingleArgParser>::SingleArgItem;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        if let Some(names) = self.name_type.names_to_register() {
            ll.register(names, H::low_level())?;
        }
        Ok(())
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        self.parse_single_arg(ll)
    }

    fn update_help(&self, help: &mut Help) {
        match self.help_message() {
            ArgHelp::Named(help_named) => help.named.push(help_named),
            ArgHelp::Positional(help_positional) => help.positional.push(help_positional),
        }
    }
}

pub struct Both<T, U, PT: Parser<Item = T>, PU: Parser<Item = U>> {
    parser_t: PT,
    parser_u: PU,
}

impl<T, U, PT: Parser<Item = T>, PU: Parser<Item = U>> Parser for Both<T, U, PT, PU> {
    type Item = (T, U);

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser_t.register_low_level(ll)?;
        self.parser_u.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        Ok((
            self.parser_t.parse_low_level(ll)?,
            self.parser_u.parse_low_level(ll)?,
        ))
    }

    fn update_help(&self, help: &mut Help) {
        self.parser_t.update_help(help);
        self.parser_u.update_help(help);
    }
}

pub struct Map<T, U, F: FnOnce(T) -> U, PT: Parser<Item = T>> {
    f: Option<F>,
    parser_t: PT,
}

impl<T, U, F: FnOnce(T) -> U, PT: Parser<Item = T>> Parser for Map<T, U, F, PT> {
    type Item = U;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser_t.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        Ok((self.f.take().expect("function has already been called"))(
            self.parser_t.parse_low_level(ll)?,
        ))
    }

    fn update_help(&self, help: &mut Help) {
        self.parser_t.update_help(help);
    }
}

pub struct Id<PT> {
    parser_t: PT,
}

impl<T, PT: Parser<Item = T>> Parser for Id<PT> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser_t.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        self.parser_t.parse_low_level(ll)
    }

    fn update_help(&self, help: &mut Help) {
        self.parser_t.update_help(help);
    }
}

impl<T, PT: Parser<Item = T>> Id<PT> {
    pub fn new(parser_t: PT) -> Self {
        Self { parser_t }
    }

    pub fn with_help_default(self) -> WithHelp<T, Self> {
        WithHelp::new_default(self)
    }
}

#[derive(Debug)]
pub enum OrHelp<T> {
    Value(T),
    Help(Help),
}

pub struct WithHelp<T, PT: Parser<Item = T>> {
    parser_t: PT,
    names: name_type::Named,
    description: String,
}

impl<T, PT: Parser<Item = T>> WithHelp<T, PT> {
    pub fn new<N: IntoName>(parser_t: PT, name: N) -> Self {
        Self {
            parser_t,
            names: name_type::Named::new(name),
            description: "print help message".to_string(),
        }
    }
    pub fn new_default(parser_t: PT) -> Self {
        Self::new(parser_t, 'h').name("help")
    }
    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.description = description.as_ref().to_string();
        self
    }
    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.names.add(name.into_name());
        self
    }
    pub fn parse_env_or_exit(self) -> T {
        match self.parse_env() {
            Ok(OrHelp::Value(item)) => item,
            Ok(OrHelp::Help(help)) => {
                println!("{}", help);
                process::exit(0);
            }
            Err((error, spent_parser)) => {
                let help = spent_parser.into_help();
                eprintln!("{}\n", error);
                eprintln!("{}", help);
                process::exit(2);
            }
        }
    }
}

impl<T, PT: Parser<Item = T>> Parser for WithHelp<T, PT> {
    type Item = OrHelp<T>;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser_t.register_low_level(ll)?;
        ll.register(self.names.names(), low_level::HasParam::No)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        match ll.get_flag_count(self.names.names()) {
            0 => Ok(OrHelp::Value(self.parser_t.parse_low_level(ll)?)),
            1 => {
                let mut help = Help::new(ll.program_name().to_string());
                self.update_help(&mut help);
                for _ in ll.free_iter() {} // drain the free arguments
                Ok(OrHelp::Help(help))
            }
            _ => Err(ParseError::ExpectedOneArgument(self.names.first_name().clone()).into()),
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.parser_t.update_help(help);
        help.named.push(ArgHelpNamed {
            names: self.names.clone(),
            hint: None,
            description: Some(self.description.clone()),
            arity: ArityEnum::Optional,
        });
    }
}

#[derive(Debug)]
pub struct ArgHelpPositional {
    pub hint: String,
    pub description: Option<String>,
    pub arity: ArityEnum,
}

#[derive(Debug)]
pub struct ArgHelpNamed {
    pub names: name_type::Named,
    pub hint: Option<String>,
    pub description: Option<String>,
    pub arity: ArityEnum,
}

#[derive(Debug)]
pub enum ArgHelp {
    Positional(ArgHelpPositional),
    Named(ArgHelpNamed),
}

#[derive(Debug)]
pub struct Help {
    pub program_name: String,
    pub positional: Vec<ArgHelpPositional>,
    pub named: Vec<ArgHelpNamed>,
}

impl Help {
    pub fn new(program_name: String) -> Self {
        Self {
            program_name,
            positional: Vec::new(),
            named: Vec::new(),
        }
    }
}

enum ValueOrDescription<T> {
    Value(T),
    Description(String),
}

pub struct WithDefaultDisplay<V: FromStr, T: fmt::Display + From<V>> {
    value_or_description: ValueOrDescription<T>,
    arg: Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>,
}

pub struct WithDefaultLazy<V: FromStr, T: From<V>, F: FnOnce() -> T> {
    thunk: Option<F>,
    description: String,
    arg: Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>,
}

pub struct WithDefaultDescribed<V: FromStr, T: From<V>> {
    value: Option<T>,
    description: String,
    arg: Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>,
}

pub struct WithDefaultParse<V: FromStr, T: From<V>> {
    value: String,
    arg: Arg<arity::Optional, has_param::YesVia<V, T>, name_type::Named>,
}

impl<V: FromStr, T: fmt::Display + From<V>> WithDefaultDisplay<V, T> {
    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.arg.description = Some(description.as_ref().to_string());
        self
    }

    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.arg.name_type.add(name.into_name());
        self
    }
}

impl<V: FromStr, T: From<V>, F: FnOnce() -> T> WithDefaultLazy<V, T, F> {
    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.arg.description = Some(description.as_ref().to_string());
        self
    }

    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.arg.name_type.add(name.into_name());
        self
    }
}

impl<V: FromStr, T: From<V>> WithDefaultDescribed<V, T> {
    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.arg.description = Some(description.as_ref().to_string());
        self
    }

    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.arg.name_type.add(name.into_name());
        self
    }
}

impl<V: FromStr, T: From<V>> WithDefaultParse<V, T> {
    pub fn desc<S: AsRef<str>>(mut self, description: S) -> Self {
        self.arg.description = Some(description.as_ref().to_string());
        self
    }

    pub fn name<N: IntoName>(mut self, name: N) -> Self {
        self.arg.name_type.add(name.into_name());
        self
    }
}

impl<V: FromStr, T: From<V>, F: FnOnce() -> T> Parser for WithDefaultLazy<V, T, F> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.arg.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        self.arg.parse_low_level(ll).map(|maybe_item| {
            maybe_item.unwrap_or_else(self.thunk.take().expect("function has already been called"))
        })
    }

    fn update_help(&self, help: &mut Help) {
        let mut help_message = match self.arg.help_message() {
            ArgHelp::Named(help_named) => help_named,
            ArgHelp::Positional(_) => panic!("named arg gave positional help message"),
        };
        help_message.description = Some(if let Some(description) = help_message.description {
            format!("{} (Default: {})", description, self.description)
        } else {
            format!("Default: {}", self.description)
        });
        help.named.push(help_message);
    }
}

impl<V: FromStr, T: fmt::Display + From<V>> Parser for WithDefaultDisplay<V, T> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.arg.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        self.arg.parse_low_level(ll).map(|maybe_item| {
            if let Some(item) = maybe_item {
                item
            } else {
                use std::mem;
                let value_or_description = mem::replace(
                    &mut self.value_or_description,
                    ValueOrDescription::Description("".to_string()),
                );
                let value = match value_or_description {
                    ValueOrDescription::Value(value) => value,
                    ValueOrDescription::Description(_) => {
                        panic!("default value has alread been used")
                    }
                };
                self.value_or_description = ValueOrDescription::Description(format!("{}", value));
                value
            }
        })
    }

    fn update_help(&self, help: &mut Help) {
        let mut help_message = match self.arg.help_message() {
            ArgHelp::Named(help_named) => help_named,
            ArgHelp::Positional(_) => panic!("named arg gave positional help message"),
        };
        let default_description = match &self.value_or_description {
            ValueOrDescription::Description(default_description) => default_description.clone(),
            ValueOrDescription::Value(value) => format!("{}", value),
        };
        help_message.description = Some(if let Some(description) = help_message.description {
            format!("{} (Default: {})", description, default_description)
        } else {
            format!("Default: {}", default_description)
        });
        help.named.push(help_message);
    }
}

impl<V: FromStr, T: From<V>> Parser for WithDefaultDescribed<V, T> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.arg.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        self.arg.parse_low_level(ll).map(|maybe_item| {
            maybe_item.unwrap_or_else(|| {
                self.value
                    .take()
                    .expect("default value has already been used")
            })
        })
    }

    fn update_help(&self, help: &mut Help) {
        let mut help_message = match self.arg.help_message() {
            ArgHelp::Named(help_named) => help_named,
            ArgHelp::Positional(_) => panic!("named arg gave positional help message"),
        };
        help_message.description = Some(if let Some(description) = help_message.description {
            format!("{} (Default: {})", description, self.description)
        } else {
            format!("Default: {}", self.description)
        });
        help.named.push(help_message);
    }
}

impl<V: FromStr, T: From<V>> Parser for WithDefaultParse<V, T> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.arg.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        if let Some(value) = self.arg.parse_low_level(ll)? {
            Ok(value)
        } else {
            Ok(self
                .value
                .parse::<V>()
                .map_err(|_| ParseError::UnableToParseArgumentParam {
                    name: self.arg.name_type.first_name().clone(),
                    value: self.value.clone(),
                })?
                .into())
        }
    }

    fn update_help(&self, help: &mut Help) {
        let mut help_message = match self.arg.help_message() {
            ArgHelp::Named(help_named) => help_named,
            ArgHelp::Positional(_) => panic!("named arg gave positional help message"),
        };
        help_message.description = Some(if let Some(description) = help_message.description {
            format!("{} (Default: {})", description, self.value)
        } else {
            format!("Default: {}", self.value)
        });
        help.named.push(help_message);
    }
}

pub struct ChooseAtMostOne<T, PA: Parser<Item = Option<T>>, PB: Parser<Item = Option<T>>>(
    pub PA,
    pub PB,
);

impl<T, PA: Parser<Item = Option<T>>, PB: Parser<Item = Option<T>>> Parser
    for ChooseAtMostOne<T, PA, PB>
{
    type Item = Option<T>;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.0.register_low_level(ll)?;
        self.1.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        let a = self.0.parse_low_level(ll)?;
        let b = self.1.parse_low_level(ll)?;
        match (a, b) {
            (Some(_), Some(_)) => Err(ParseError::MultipleMutuallyExclusiveOptionsChosen.into()),
            (Some(a), None) => Ok(Some(a)),
            (None, Some(b)) => Ok(Some(b)),
            (None, None) => Ok(None),
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.0.update_help(help);
        self.1.update_help(help);
    }
}

impl<V: FromStr, T: From<V>, N: NameType> Arg<arity::Optional, has_param::YesVia<V, T>, N>
where
    Self: SingleArgParser<SingleArgItem = Option<T>> + SingleArgParserHelp,
{
    pub fn choose_at_most_one<O: Parser<Item = Option<T>>>(
        self,
        other: O,
    ) -> ChooseAtMostOne<T, Self, O> {
        ChooseAtMostOne(self, other)
    }
}

impl<T, PA: Parser<Item = Option<T>>, PB: Parser<Item = Option<T>>> ChooseAtMostOne<T, PA, PB> {
    pub fn choose_at_most_one<O: Parser<Item = Option<T>>>(
        self,
        other: O,
    ) -> ChooseAtMostOne<T, Self, O> {
        ChooseAtMostOne(self, other)
    }
}

impl<T> SomeIf<T> {
    pub fn choose_at_most_one<O: Parser<Item = Option<T>>>(
        self,
        other: O,
    ) -> ChooseAtMostOne<T, Self, O> {
        ChooseAtMostOne(self, other)
    }
}

pub struct SomeIf<T> {
    value: Option<T>,
    arg: Arg<arity::Optional, has_param::No, name_type::Named>,
}

impl<T> Parser for SomeIf<T> {
    type Item = Option<T>;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.arg.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        if self.arg.parse_low_level(ll)? {
            Ok(Some(self.value.take().expect("value already used")))
        } else {
            Ok(None)
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.arg.update_help(help);
    }
}

pub struct WithGeneralDefault<T, P: Parser> {
    value: Option<T>,
    parser: P,
}

impl<T, P: Parser<Item = Option<T>>> Parser for WithGeneralDefault<T, P> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        if let Some(value) = self.parser.parse_low_level(ll)? {
            Ok(value)
        } else {
            Ok(self.value.take().expect("value already used"))
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.parser.update_help(help);
    }
}

pub struct WithGeneralDefaultLazy<T, F: FnOnce() -> T, P: Parser> {
    value: Option<F>,
    parser: P,
}

impl<T, F: FnOnce() -> T, P: Parser<Item = Option<T>>> Parser for WithGeneralDefaultLazy<T, F, P> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        if let Some(value) = self.parser.parse_low_level(ll)? {
            Ok(value)
        } else {
            Ok((self.value.take().expect("function already called"))())
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.parser.update_help(help);
    }
}

pub struct GeneralRequired<P: Parser> {
    parser: P,
    error_message: String,
}

impl<T, P: Parser<Item = Option<T>>> Parser for GeneralRequired<P> {
    type Item = T;

    fn register_low_level(&self, ll: &mut low_level::LowLevelParser) -> Result<(), SpecError> {
        self.parser.register_low_level(ll)
    }

    fn parse_low_level(
        &mut self,
        ll: &mut low_level::LowLevelParserOutput,
    ) -> Result<Self::Item, Box<dyn error::Error>> {
        if let Some(value) = self.parser.parse_low_level(ll)? {
            Ok(value)
        } else {
            Err(ParseError::MissingRequiredArgumentGeneral(self.error_message.clone()).into())
        }
    }

    fn update_help(&self, help: &mut Help) {
        self.parser.update_help(help);
    }
}

impl fmt::Display for Help {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        const ARG_SINGLE_LINE_MAX_ARG_LENGTH: usize = 16;
        const OPT_SINGLE_LINE_MAX_ARG_LENGTH: usize = 32;
        const DESCRIPTION_LEFT_PAD: usize = 4;
        write!(f, "Usage: {} [OPTIONS]", self.program_name)?;
        for p in &self.positional {
            match p.arity {
                ArityEnum::Required => write!(f, " {}", p.hint)?,
                ArityEnum::Optional => write!(f, " [{}]", p.hint)?,
                ArityEnum::Multiple => write!(f, " [{} ...]", p.hint)?,
            }
        }
        if !self.positional.is_empty() {
            let parts = self
                .positional
                .iter()
                .map(|p| {
                    let hint = match p.arity {
                        ArityEnum::Required => format!("{}", p.hint),
                        ArityEnum::Optional => format!("[{}]", p.hint),
                        ArityEnum::Multiple => format!("[{} ...]", p.hint),
                    };
                    (hint, p.description.as_ref())
                })
                .collect::<Vec<_>>();
            let max_hint_width = parts
                .iter()
                .filter_map(|p| {
                    if p.0.len() < ARG_SINGLE_LINE_MAX_ARG_LENGTH {
                        Some(p.0.len())
                    } else {
                        None
                    }
                })
                .max()
                .unwrap_or(0)
                + DESCRIPTION_LEFT_PAD;
            write!(f, "\n\nArgs:")?;
            for (hint, description) in parts {
                writeln!(f)?;
                if let Some(description) = description {
                    if hint.len() < ARG_SINGLE_LINE_MAX_ARG_LENGTH {
                        write!(
                            f,
                            "    {:width$} {}",
                            hint,
                            description,
                            width = max_hint_width,
                        )?;
                    } else {
                        writeln!(f, "    {}", hint)?;
                        write!(f, "                {}", description)?;
                    }
                } else {
                    write!(f, "    {}", hint)?;
                }
            }
        }
        if !self.named.is_empty() {
            let parts = self
                .named
                .iter()
                .map(|n| {
                    let name_list = n
                        .names
                        .names()
                        .iter()
                        .map(|name| name.to_string())
                        .collect::<Vec<_>>()
                        .join(", ");
                    let name_list_with_arity = match (n.arity, n.hint.as_ref()) {
                        (ArityEnum::Required, None) => format!("{}", name_list),
                        (ArityEnum::Optional, None) => format!("[{}]", name_list),
                        (ArityEnum::Multiple, None) => format!("[{} ...]", name_list),
                        (ArityEnum::Required, Some(hint)) => format!("{} {}", name_list, hint),
                        (ArityEnum::Optional, Some(hint)) => format!("[{} {}]", name_list, hint),
                        (ArityEnum::Multiple, Some(hint)) => {
                            format!("[{} {} ...]", name_list, hint)
                        }
                    };
                    (name_list_with_arity, n.description.as_ref())
                })
                .collect::<Vec<_>>();
            let max_name_list_width = parts
                .iter()
                .filter_map(|p| {
                    if p.0.len() < OPT_SINGLE_LINE_MAX_ARG_LENGTH {
                        Some(p.0.len())
                    } else {
                        None
                    }
                })
                .max()
                .unwrap_or(0)
                + DESCRIPTION_LEFT_PAD;
            write!(f, "\n\nOptions:")?;
            for (name_list, description) in parts {
                writeln!(f)?;
                if let Some(description) = description {
                    if name_list.len() < OPT_SINGLE_LINE_MAX_ARG_LENGTH {
                        write!(
                            f,
                            "    {:width$} {}",
                            name_list,
                            description,
                            width = max_name_list_width,
                        )?;
                    } else {
                        writeln!(f, "    {}", name_list)?;
                        write!(f, "                {}", description)?;
                    }
                } else {
                    write!(f, "    {}", name_list)?;
                }
            }
        }
        Ok(())
    }
}