argster 0.1.1

A simple parser for command line arguments
Documentation
use std::{path::PathBuf, str::FromStr};

use super::*;

macro_rules! num_from_args {
    ($type:ty, $ref:literal) => {
        impl FromArgsItem for $type {
            const TYPE_NAME: &'static str = stringify!($type);
            const TYPE_DESC: &'static str = $ref;

            fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error> {
                match item {
                    Some(ArgsItem::String(s)) => s.parse().map_err(|ex| Error::InvalidType {
                        arg: "".into(),
                        expected: $ref.into(),
                        found: format!("string: {}", ex),
                    }),
                    Some(ArgsItem::Present) => Err(Error::InvalidType {
                        arg: "".into(),
                        expected: $ref.into(),
                        found: "flag".into(),
                    }),
                    Some(ArgsItem::Many(_)) => Err(Error::InvalidType {
                        arg: "".into(),
                        expected: $ref.into(),
                        found: "list".into(),
                    }),
                    Some(ArgsItem::PresentTimes(n)) => Ok(*n as $type),
                    None => Err(Error::NotFound("".into())),
                }
            }
        }
    };
}

impl FromArgsItem for String {
    const TYPE_DESC: &'static str = "<string>";
    const TYPE_NAME: &'static str = "String";

    fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error> {
        match item {
            Some(ArgsItem::String(s)) => Ok(s.to_string()),
            Some(ArgsItem::Present) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "string".into(),
                found: "flag".into(),
            }),
            Some(ArgsItem::Many(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "string".into(),
                found: "list".into(),
            }),
            Some(ArgsItem::PresentTimes(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "string".into(),
                found: "flag ".into(),
            }),
            None => Err(Error::NotFound("string".into())),
        }
    }
}

impl FromArgsItem for bool {
    const TYPE_NAME: &'static str = "bool";
    const TYPE_DESC: &'static str = "<true|false>";
    const TYPE_EXTRA: &'static str = "flag";

    fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error> {
        match item {
            Some(ArgsItem::String(s)) => s.parse().map_err(|ex| Error::InvalidType {
                arg: "string".into(),
                expected: "bool".into(),
                found: format!("string: {}", ex),
            }),
            Some(ArgsItem::Present) => Ok(true),
            Some(ArgsItem::Many(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "true|false".into(),
                found: "list".into(),
            }),
            Some(ArgsItem::PresentTimes(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "true|false".into(),
                found: "flag ".into(),
            }),
            None => Ok(false),
        }
    }
}

impl FromArgsItem for PathBuf {
    const TYPE_DESC: &'static str = "<path>";
    const TYPE_NAME: &'static str = "PathBuf";

    fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error>
    where
        Self: Sized,
    {
        match item {
            Some(ArgsItem::String(s)) => {
                Ok(PathBuf::from_str(s).map_err(|ex| Error::InvalidType {
                    arg: "string".into(),
                    expected: "path".into(),
                    found: format!("string: {}", ex),
                })?)
            }
            Some(ArgsItem::Present) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "path".into(),
                found: "flag".into(),
            }),
            Some(ArgsItem::Many(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "path".into(),
                found: "list".into(),
            }),
            Some(ArgsItem::PresentTimes(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "path".into(),
                found: "flag ".into(),
            }),
            None => Err(Error::NotFound("string".into())),
        }
    }
}

impl<T> FromArgsItem for Option<T>
where
    T: FromArgsItem,
{
    const TYPE_NAME: &'static str = T::TYPE_NAME;
    const TYPE_DESC: &'static str = T::TYPE_DESC;
    const TYPE_EXTRA: &'static str = "optinal";

    fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error>
    where
        Self: Sized,
    {
        item.map(|x| T::from_args_item(Some(x))).transpose()
    }
}

impl<T> FromArgsItem for Vec<T>
where
    T: FromArgsItem,
{
    const TYPE_NAME: &'static str = T::TYPE_NAME;
    const TYPE_DESC: &'static str = T::TYPE_DESC;
    const TYPE_EXTRA: &'static str = "list";

    fn from_args_item(item: Option<&ArgsItem>) -> Result<Self, Error> {
        match item {
            Some(ArgsItem::String(s)) => s
                .split(',')
                .map(|x| T::from_args_item(Some(&ArgsItem::String(x.to_string()))))
                .collect::<Result<Vec<_>, _>>(),
            Some(ArgsItem::Present) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "list".into(),
                found: "flag".into(),
            }),
            Some(ArgsItem::Many(list)) => list
                .iter()
                .map(|x| T::from_args_item(Some(&ArgsItem::String(x.to_string()))))
                .collect::<Result<Vec<_>, _>>(),
            Some(ArgsItem::PresentTimes(_)) => Err(Error::InvalidType {
                arg: "string".into(),
                expected: "list".into(),
                found: "flag ".into(),
            }),
            None => Err(Error::NotFound("string".into())),
        }
    }
}

num_from_args!(u8, "<positive number>");
num_from_args!(u16, "<positive number>");
num_from_args!(u32, "<positive number>");
num_from_args!(u64, "<positive number>");
num_from_args!(u128, "<positive number>");
num_from_args!(usize, "<positive number>");

num_from_args!(i8, "<number>");
num_from_args!(i16, "<number>");
num_from_args!(i32, "<number>");
num_from_args!(i64, "<number>");
num_from_args!(i128, "<number>");
num_from_args!(isize, "<number>");

num_from_args!(f64, "<decimal>");
num_from_args!(f32, "<decimal>");