clap-serde 0.5.0

Provides a wrapper to deserialize clap app using serde.
Documentation
use clap::builder::ValueParser as VP;
use serde::Deserialize;

macro_rules! enum_de_value {
    ($basety : ident, $newty :ident,
        $(#[$derive_meta:meta])*
        {
            $( $(
                #[ $cfg_meta_ex:meta ] )?
                $var_ex: ident $( { $( $(#[$cfg_v:meta])* $vx: ident : $vt: ty ),* } )?
                    => $to_ex: expr
            ,)*
        }
        {
            $(($pty: ty, $pty_upper : tt $(, $ty_as: ty)?)),*
        }
    ) => {
        enum_de!($basety, $newty,
            $(#[$derive_meta])* {}
            {
                $( $(
                    #[ $cfg_meta_ex ] )?
                    $var_ex $( { $( $(#[$cfg_v])* $vx : $vt ),* } )?
                        => $to_ex
                ,)*
                $(
                $pty_upper {
                    #[serde(skip_serializing_if = "Option::is_none")]
                    min: Option<$pty>,
                    #[serde(skip_serializing_if = "Option::is_none")]
                    max: Option<$pty>,
                    #[serde(default = "get_true")]
                    max_inclusive: bool
                } => {
                    match (min, max, max_inclusive) {
                        (Some(s), Some(e), false) => clap::value_parser!($pty).range((s $(as $ty_as)*) ..(e $(as $ty_as)*)).into(),
                        (Some(s), Some(e), true) => clap::value_parser!($pty).range((s $(as $ty_as)*) ..=(e $(as $ty_as)*)).into(),
                        (Some(s), None, _) => clap::value_parser!($pty).range((s $(as $ty_as)*)..).into(),
                        (None, Some(e), false) => clap::value_parser!($pty).range(..(e $(as $ty_as)*)).into(),
                        (None, Some(e), true) => clap::value_parser!($pty).range(..=(e $(as $ty_as)*)).into(),
                        (None, None, _) => clap::value_parser!($pty).into(),
                    }
                },)*
            }
        );
    };
}

const fn get_true() -> bool {
    true
}

enum_de_value!(VP, ValueParser1,
    #[derive(Deserialize, Clone, Copy)]
    #[serde(tag = "type")]
    #[cfg_attr(feature = "kebab-case-key" ,serde(rename_all = "kebab-case"))]
    #[cfg_attr(feature = "snake-case-key" ,serde(rename_all = "snake_case"))]
    {
        Bool => {
            VP::bool()
        },
        String => {
            VP::string()
        },
        OsString => {
            VP::os_string()
        },
        PathBuf => {
            VP::path_buf()
        },
        Boolish => {
            clap::builder::BoolishValueParser::new().into()
        },
        Falsey => {
            clap::builder::FalseyValueParser::new().into()
        },
        NonEmptyString => {
            clap::builder::NonEmptyStringValueParser::new().into()
        },
    }
    {
        (i64, I64),
        (i32, I32, i64),
        (i16, I16, i64),
        (i8 , I8, i64),
        (u64, U64),
        (u32, U32, i64),
        (u16, U16, i64),
        (u8 , U8, i64)
    }
);

enum_de!(VP, ValueParser2,
    #[derive(Deserialize, Clone, Copy)]
    #[cfg_attr(feature = "kebab-case-key" ,serde(rename_all = "kebab-case"))]
    #[cfg_attr(feature = "snake-case-key" ,serde(rename_all = "snake_case"))]
    {}
    {
        Bool => {
            VP::bool()
        },
        String => {
            VP::string()
        },
        OsString => {
            VP::os_string()
        },
        PathBuf => {
            VP::path_buf()
        },
        Boolish => {
            clap::builder::BoolishValueParser::new().into()
        },
        Falsey => {
            clap::builder::FalseyValueParser::new().into()
        },
        NonEmptyString => {
            clap::builder::NonEmptyStringValueParser::new().into()
        },
        I64 => {
            clap::value_parser!(i64).into()
        },
        I32 => {
            clap::value_parser!(i32).into()
        },
        I16 => {
            clap::value_parser!(i16).into()
        },
        I8 => {
            clap::value_parser!(i8).into()
        },
        U64 => {
            clap::value_parser!(u64).into()
        },
        U32 => {
            clap::value_parser!(u32).into()
        },
        U16 => {
            clap::value_parser!(u16).into()
        },
        U8 => {
            clap::value_parser!(u8).into()
        },
    }
);

#[derive(Deserialize)]
#[serde(untagged)]
pub(crate) enum ValueParser {
    Value(ValueParser2),
    Tagged(ValueParser1),
}

impl From<ValueParser> for VP {
    fn from(v: ValueParser) -> Self {
        match v {
            ValueParser::Value(v) => v.into(),
            ValueParser::Tagged(t) => t.into(),
        }
    }
}