derive_num_bounded/
lib.rs

1#![allow(unused_macros)]
2#![allow(unused_imports)]
3
4//! Macros for implementing bounded number types.
5
6pub use paste;
7pub use thiserror;
8
9#[macro_export]
10macro_rules! derive_new_from_bounded_partial_ord {
11    ( $type:ident < $a:ty : $bound:ident > ) => {
12        $crate::_derive_new_from_bounded_partial_ord!(
13            $type<$a: $bound>,
14            $a,
15            IsIncomparable,
16            "incomparable"
17        );
18    };
19    ( $type:ident {( $inner:ty )} ) => {
20        $crate::_derive_new_from_bounded_partial_ord!(
21            $type,
22            $inner,
23            IsIncomparable,
24            "incomparable"
25        );
26    };
27}
28
29#[macro_export]
30macro_rules! derive_new_from_bounded_float {
31    ( $type:ident < $a:ty : $bound:ident > ) => {
32        $crate::_derive_new_from_bounded_partial_ord!($type<$a: $bound>, $a, IsNan, "NaN");
33    };
34    ( $type:ident ( $inner:ty ) ) => {
35        $crate::_derive_new_from_bounded_partial_ord!($type, $inner, IsNan, "NaN");
36    };
37}
38
39#[macro_export]
40macro_rules! _derive_new_from_bounded_partial_ord {
41    ( $type:ident $( < $a:ty : $bound:ident > )?, $inner:ty, $incomparable_name:ident, $incomparable_str:literal ) => {
42        $crate::paste::paste! {
43            #[doc = "Error returned when '" $type "' is given an invalid value."]
44            #[derive(Clone, Copy, Debug, $crate::thiserror::Error, PartialEq)]
45            pub enum [<Invalid $type Error>] $(< $a : $bound >)? {
46                #[doc = "Value is " $incomparable_str "."]
47                #[error("{0} is {}", $incomparable_str)]
48                $incomparable_name($inner),
49                /// Value is below lower bound.
50                #[error("{0} is below lower bound ({})", < $type $(< $a >)? > ::min_value())]
51                TooLow($inner),
52                /// Value is above upper bound.
53                #[error("{0} is above upper bound ({})", < $type $(< $a >)? > ::max_value())]
54                TooHigh($inner),
55            }
56
57            impl $(< $a : $bound >)? $type $(< $a >)? {
58                #[doc = "Return a new '" $type "' if given a valid value."]
59                pub fn new(value: $inner) -> Result<Self, [<Invalid $type Error>]  $(< $a >)? > {
60                    match (
61                        Self(value).partial_cmp(&Self::min_value()),
62                        Self(value).partial_cmp(&Self::max_value()),
63                    ) {
64                        (None, _) | (_, None) => Err([<Invalid $type Error>]::$incomparable_name(value)),
65                        (Some(std::cmp::Ordering::Less), _) => Err([<Invalid $type Error>]::TooLow(value)),
66                        (_, Some(std::cmp::Ordering::Greater)) => Err([<Invalid $type Error>]::TooHigh(value)),
67                        _ => Ok(Self(value)),
68                    }
69                }
70            }
71        }
72    };
73}
74
75#[macro_export]
76macro_rules! derive_new_from_lower_bounded_partial_ord {
77    ( $type:ident < $a:ty : $bound:ident > ) => {
78        $crate::_derive_new_from_lower_bounded_partial_ord!(
79            $type<$a: $bound>,
80            $a,
81            IsIncomparable,
82            "incomparable"
83        );
84    };
85    ( $type:ident {( $inner:ty )} ) => {
86        $crate::_derive_new_from_lower_bounded_partial_ord!(
87            $type,
88            $inner,
89            IsIncomparable,
90            "incomparable"
91        );
92    };
93}
94
95#[macro_export]
96macro_rules! derive_new_from_lower_bounded_float {
97    ( $type:ident < $a:ty : $bound:ident > ) => {
98        $crate::_derive_new_from_lower_bounded_partial_ord!($type<$a: $bound>, $a, IsNan, "NaN");
99    };
100    ( $type:ident ( $inner:ty ) ) => {
101        $crate::_derive_new_from_lower_bounded_partial_ord!($type, $inner, IsNan, "NaN");
102    };
103}
104
105#[macro_export]
106macro_rules! _derive_new_from_lower_bounded_partial_ord {
107    ( $type:ident $( < $a:ty : $bound:ident > )?, $inner:ty, $incomparable_name:ident, $incomparable_str:literal ) => {
108        $crate::paste::paste! {
109            #[doc = "Error returned when '" $type "' is given an invalid value."]
110            #[derive(Clone, Copy, Debug, $crate::thiserror::Error, PartialEq, Eq)]
111            pub enum [<Invalid $type Error>] $(< $a : $bound >)? {
112                #[doc = "Value is " $incomparable_str "."]
113                #[error("{0} is {}", $incomparable_str)]
114                $incomparable_name($inner),
115                /// Value is below lower bound.
116                #[error("{0} is below lower bound ({})", < $type $(< $a >)? > ::min_value())]
117                TooLow($inner),
118            }
119
120            impl $(< $a : $bound >)? $type $(< $a >)? {
121                #[doc = "Return a new '" $type "' if given a valid value."]
122                pub fn new(value: $inner) -> Result<Self, [<Invalid $type Error>] $(< $a >)? > {
123                    match Self(value).partial_cmp(&Self::min_value()) {
124                        None => Err([<Invalid $type Error>]::$incomparable_name(value)),
125                        Some(std::cmp::Ordering::Less) => Err([<Invalid $type Error>]::TooLow(value)),
126                        _ => Ok(Self(value)),
127                    }
128                }
129            }
130        }
131    };
132}
133
134#[macro_export]
135macro_rules! derive_new_from_lower_bounded {
136    ( $type:ident ( $inner: ty ) ) => {
137        $crate::paste::paste! {
138            #[doc = "Error returned when '" $type "' is given a value below lower bound."]
139            #[derive(Clone, Copy, Debug, $crate::thiserror::Error)]
140            #[error("{0} is below lower bound ({})", $type::min_value())]
141            pub struct [<Invalid $type Error>]($inner);
142
143            impl $type {
144                #[doc = "Return a new '" $type "' if given a valid value."]
145                pub fn new(value: $inner) -> Result<Self, [<Invalid $type Error>]> {
146                    if Self(value) < Self::min_value() {
147                        Err([<Invalid $type Error>](value))
148                    } else {
149                        Ok(Self(value))
150                    }
151                }
152            }
153        }
154    };
155}
156
157#[macro_export]
158macro_rules! derive_try_from_from_new {
159    ( $type:ident ( $inner:ty ) ) => {
160        $crate::paste::paste! {
161            impl core::convert::TryFrom<$inner> for $type {
162                type Error = [<Invalid $type Error>];
163                fn try_from(value: $inner) -> Result<Self, Self::Error> {
164                    $type::new(value)
165                }
166            }
167        }
168    };
169}
170
171#[macro_export]
172macro_rules! derive_from_str_from_try_into {
173    ( $type:ident ( $inner:ty ) ) => {
174        $crate::paste::paste! {
175            #[doc = "Error returned when failing to convert from a string or into '" $type "'."]
176            #[derive(Clone, Debug, $crate::thiserror::Error)]
177            #[error(transparent)]
178            pub struct [<$type FromStrError>](#[from] [<_ $type FromStrError>]);
179
180            #[derive(Clone, Debug, $crate::thiserror::Error)]
181            enum [<_ $type FromStrError>] {
182                #[doc = "Error convering from 'str' to '" $inner "'."]
183                #[error("Failed to convert from 'str': {0}")]
184                FromStr(<$inner as std::str::FromStr>::Err),
185                #[doc = "Error convering from '" $inner "' to '" $type "'."]
186                #[error("Failed to convert into type: {0}")]
187                TryInto(<$type as TryFrom<$inner>>::Error),
188            }
189
190            impl std::str::FromStr for $type {
191                type Err = [<$type FromStrError>];
192
193                fn from_str(s: &str) -> Result<Self, Self::Err> {
194                    s.parse::<$inner>()
195                        .map_err([<_ $type FromStrError>]::FromStr)
196                        .and_then(|x| x.try_into().map_err([<_ $type FromStrError>]::TryInto))
197                        .map_err(|x| x.into())
198                }
199            }
200        }
201    };
202}
203
204#[macro_export]
205macro_rules! derive_into_inner {
206    ( $type:ident ( $inner:ty ) ) => {
207        $crate::paste::paste! {
208            impl $type {
209                #[doc = "Unwrap '" $type "' into inner value."]
210                pub fn into_inner(self) -> $inner {
211                    self.0
212                }
213            }
214        }
215    };
216    ( $type:ident < $a:ty > ) => {
217        $crate::paste::paste! {
218            impl < $a > $type < $a > {
219                #[doc = "Unwrap '" $type "' into inner value."]
220                pub fn into_inner(self) -> $a {
221                    self.0
222                }
223            }
224        }
225    };
226}