validators-derive 0.26.0

The provided crate offers a procedural macro for defining validators, including optional parameters.
use std::{fmt::Display, str::FromStr};

use syn::Meta;

use crate::common::{
    path_to_string,
    range::{Range, RangedNumber, range_equal},
};

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum RangeOption<T: FromStr + Copy + PartialOrd + Display>
where
    T::Err: Display, {
    Inside { min: Option<T>, max: Option<T>, inclusive: bool },
    Outside { min: Option<T>, max: Option<T>, inclusive: bool },
    Unlimited,
}

impl<T: RangedNumber> RangeOption<T>
where
    T::Err: Display,
{
    #[inline]
    pub(crate) fn from_meta(meta: &Meta) -> syn::Result<Self> {
        debug_assert!(meta.path().is_ident("range"));

        if let Meta::List(list) = meta {
            let meta: Meta = list.parse_args()?;

            if let Some(ident) = meta.path().get_ident() {
                match ident.to_string().as_str() {
                    "Inside" => {
                        let Range {
                            min,
                            max,
                            inclusive,
                        } = Range::from_meta(&meta)?;

                        return Ok(Self::Inside {
                            min,
                            max,
                            inclusive,
                        });
                    },
                    "Outside" => {
                        let Range {
                            min,
                            max,
                            inclusive,
                        } = Range::from_meta(&meta)?;

                        return Ok(Self::Outside {
                            min,
                            max,
                            inclusive,
                        });
                    },
                    "Unlimited" => return Ok(Self::Unlimited),
                    _ => (),
                }
            }
        }

        let path = meta.path();

        Err(syn::Error::new_spanned(
            path,
            format!(
                "expected `{path}(Inside(..)/Outside(..)/Unlimited)`",
                path = path_to_string(path)
            ),
        ))
    }
}

pub(crate) enum RangeTokenStream {
    Inside {
        min:       Option<proc_macro2::TokenStream>,
        max:       Option<proc_macro2::TokenStream>,
        inclusive: bool,
        equal:     bool,
    },
    Outside {
        min:       Option<proc_macro2::TokenStream>,
        max:       Option<proc_macro2::TokenStream>,
        inclusive: bool,
        equal:     bool,
    },
    Unlimited,
}

impl RangeTokenStream {
    #[inline]
    pub(crate) fn inside(&self) -> bool {
        matches!(self, Self::Inside { .. })
    }
}

impl<T: RangedNumber> From<RangeOption<T>> for RangeTokenStream
where
    T::Err: Display,
{
    fn from(value: RangeOption<T>) -> Self {
        match value {
            RangeOption::Inside {
                min,
                max,
                inclusive,
            } => Self::Inside {
                min: min.map(|a| a.into_token_stream()),
                max: max.map(|a| a.into_token_stream()),
                inclusive,
                equal: range_equal(min, max, inclusive),
            },
            RangeOption::Outside {
                min,
                max,
                inclusive,
            } => Self::Outside {
                min: min.map(|a| a.into_token_stream()),
                max: max.map(|a| a.into_token_stream()),
                inclusive,
                equal: range_equal(min, max, inclusive),
            },
            RangeOption::Unlimited => Self::Unlimited,
        }
    }
}