constraint 0.1.0

Easly create types with the guarantees you need
Documentation
#[doc = include_str!("../README.md")]

/// Declares a new constrained type.
///
/// # Examples
///
/// A bounded `i32` type:
/// ```
/// use std::convert::TryFrom;
/// constraint::constraint!{
///     /// Some documentation, must be inside the macro
///     // You can also add any meta attributes
///     #[derive(Default)]
///     pub BoundedI32(i32) if |x: &i32| *x > 2 && *x < 5
/// }
///
/// assert!(BoundedI32::try_from(2).is_err());
/// assert!(BoundedI32::try_from(4).is_ok());
/// ```
#[macro_export]
macro_rules! constraint {
    (
        $(#[$outer:meta])*
        $vis:vis $name:ident($typ:ident) if $cond:expr
    ) => {
        $(#[$outer])*
        $vis struct $name($typ);

        impl $name {
            #[allow(dead_code)]
            pub fn get(&self) -> &$typ {
                &self.0
            }

            #[allow(dead_code)]
            pub fn get_mut(&mut self) ->&mut $typ {
                &mut self.0
            }

            #[allow(dead_code)]
            pub fn into_inner(self) -> $typ {
                self.0
            }
        }

        impl std::convert::TryFrom<$typ> for $name {
            type Error = ();

            fn try_from(value: $typ) -> Result<Self, Self::Error> {
                if $cond(&value) {
                    Ok(Self(value))
                }
                else {
                    Err(())
                }
            }
        }
    };
}

#[cfg(test)]
mod tests {
    use std::convert::TryFrom;

    #[test]
    fn compile_bounded_i32() {
        crate::constraint! { pub(crate) Bounded(i32) if |value: &i32| *value < 5 && *value > 2 }

        assert!(Bounded::try_from(2).is_err());
        assert!(Bounded::try_from(3).is_ok());
        assert!(Bounded::try_from(4).is_ok());
        assert!(Bounded::try_from(5).is_err());
    }
}