1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// devela::num::non_range
//
//! Creates const generic customizable wrappers over the `NonZero` primitives.
//

use crate::meta::{iif, paste};
use core::{fmt, num::*, str::FromStr};

#[cfg(all(feature = "bytemuck", feature = "unsafe_num"))]
use bytemuck::{CheckedBitPattern, NoUninit, PodInOption, ZeroableInOption};

impl_non_range![NonRange];
macro_rules! impl_non_range {
    // Entry point, generates NonRange structures for each sign and size.
    ($name:ident) => {
        impl_non_range![$name, "A signed", i, 8, 16, 32, 64, 128, size];
        impl_non_range![$name, "An unsigned", u, 8, 16, 32, 64, 128, size];
    };
    ($name:ident, $doc:literal, $s:ident, $( $b:expr ),+) => {
        $( impl_non_range![@$name, $doc, $s, $b]; )+
    };

    // $name: the base name of the new type. E.g. NonRange.
    // $doc:  the specific beginning of the documentation.
    // $s:    the sign identifier, lowercase: i or u.
    // $b:    the bits of the type, from 8 to 128, or the `size` suffix.
    (@$name:ident, $doc:literal, $s:ident, $b:expr) => { paste! {
        /* definition */

        #[doc = $doc " integer that is known to be excluded from some inclusive range." ]
        ///
        /// It has an optimized memory layout, so that
        #[doc = "`Option<"[<$name $s:upper $b>]">` is the same size as `"[<$name $s:upper $b>]"`."]
        ///
        /// # Examples
        /// ```
        #[doc = "use devela::num::" [<$name $s:upper $b>] ";"]
        ///
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::new(5).is_none()];"]
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::new(25).is_none()];"]
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::new(4).unwrap().get() == 4];"]
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::new(26).unwrap().get() == 26];"]
        ///
        #[doc = "// Self::INVALID_VALUES + Self::VALID_VALUES == "[<u $b>]"::MAX + 1."]
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::VALID_VALUES == "
            [<u $b>]"::MAX - 21 + 1];"]
        #[doc = "assert![" [<$name $s:upper $b>] "::<5, 25>::INVALID_VALUES == 21];"]
        /// ```
        ///
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        #[repr(C)]
        pub struct [<$name $s:upper $b>]<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            ([<NonZero $s:upper $b>]);

        /* methods */

        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]> [<$name $s:upper $b>]<RMIN, RMAX> {
            #[doc = "Returns a `" [<$name $s:upper $b>] "` with the given `value`,"
            " only if it's **not** between `RMIN` and `RMAX`."]
            ///
            /// Returns `None` if `value` is between `RMIN` and `RMAX`, inclusive,
            /// or if `RMIN > RMAX`.
            #[must_use]
            pub const fn new(value: [<$s:lower $b>]) -> Option<Self> {
                if RMIN <= RMAX && (value < RMIN || value > RMAX) {
                    match [<NonZero $s:upper $b>]::new(value ^ RMIN) {
                        None => None,
                        Some(v) => Some(Self(v)),
                    }
                } else {
                    None
                }
            }

            #[doc = "Returns a `" [<$name $s:upper $b>]
            "` if the given value is **not** between `RMIN` and `RMAX`."]
            ///
            /// # Panics
            /// Panics in debug if the given `value` is between `RMIN` and `RMAX`,
            /// inclusive, or if `RMIN > RMAX`.
            /// # Safety
            /// The given `value` must never be between `RMIN` and `RMAX`, inclusive.
            #[must_use]
            #[cfg(feature = "unsafe_num")]
            #[cfg_attr(feature = "nightly", doc(cfg(feature = "unsafe_num")))]
            pub const unsafe fn new_unchecked(value: [<$s:lower $b>]) -> Self {
                #[cfg(debug_assertions)]
                if RMIN > RMAX {
                    panic!("RMIN must be less or equal than RMAX.")
                }
                #[cfg(debug_assertions)]
                if value >= RMIN || value <= RMAX {
                    panic!("The given value was inside the given prohibited range.")
                }
                Self([<NonZero $s:upper $b>]::new_unchecked(value ^ RMIN))
            }

            /// Returns the value as a primitive type.
            #[must_use]
            #[inline]
            pub const fn get(&self) -> [<$s:lower $b>] {
                self.0.get() ^ RMIN
            }

            // The bit representation of `RMIN`.
            #[allow(unused)]
            const RMIN_BITS: [<$s:lower $b>] = 0;

            // The bit representation of `RMAX`.
            #[allow(unused)]
            const RMAX_BITS: [<$s:lower $b>] = {
                iif![RMIN > RMAX; panic!("RMIN must be less or equal than RMAX.")];
                RMAX.wrapping_sub(RMIN)
            };

            /// Returns the number of valid values outside the range from `RMIN` to `RMAX`,
            /// inclusive, as an unsigned integer with equal bit size.
            ///
            /// # Notice
            /// A range where `RMAX == RMIN` will result in `VALID_VALUES ==
            #[doc = "`[`" [<u $b>]"::MAX`], as expected."]
            ///
            #[doc = "A range from [`"[<$s:lower $b>]"::MIN`] to [`"[<$s:lower $b>]"::MAX`] will result"]
            /// in `VALID_VALUES == 0`, also as expected.
            /// Just be aware that in this case `INVALID_VALUES` will also be `== 0` instead of
            #[doc = " [`" [<u $b>]"::MAX`]` + 1`."]
            pub const VALID_VALUES: [<u $b>] = ([<u $b>]::MAX - Self::INVALID_VALUES)
                .wrapping_add(1);

            /// Returns the number of invalid values in the range from `RMIN` to `RMAX`,
            /// inclusive, as an unsigned integer with equal bit size.
            ///
            /// # Notice
            /// A range where `RMAX == RMIN` will result in `INVALID_VALUES == 1`, as expected.
            ///
            #[doc = "A range from [`"[<$s:lower $b>]"::MIN`] to [`"[<$s:lower $b>]"::MAX`] will result"
            " in `INVALID_VALUES == 0` instead of [`"[<u $b>]"::MAX`]` + 1`."]
            /// This is because the maximum number of representable values for a given
            /// bit-size can't be represented using the same number of bits.
            /// In this case `VALID_VALUES` will also be `== 0`.
            ///
            #[doc = "This doesn't matter as much because a [`"[<$name $s:upper $b>]"`] with the"]
            /// largest range of invalid values can't ever be constructed.
            ///
            #[doc = "Remember that `INVALID_VALUES + VALID_VALUES == "[<u $b>]"::MAX + 1`,"]
            /// which would wrap to `0`.
            pub const INVALID_VALUES: [<u $b>] = {
                iif![RMIN > RMAX; panic!("RMIN must be less or equal than RMAX.")];
                RMAX.wrapping_add((RMIN.wrapping_neg()).wrapping_add(1)) as [<u $b>]
            };
        }

        /* core impls */

        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::Display for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.get())
            }
        }
        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::Debug for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}::<{}, {}>({})", stringify!([<$name $s:upper $b>]), RMIN, RMAX, self.get())
            }
        }
        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::Binary for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Binary::fmt(&self.get(), f)
            }
        }
        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::Octal for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::Octal::fmt(&self.get(), f)
            }
        }
        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::LowerHex for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::LowerHex::fmt(&self.get(), f)
            }
        }
        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            fmt::UpperHex for [<$name $s:upper $b>]<RMIN, RMAX> {
            #[inline]
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                fmt::UpperHex::fmt(&self.get(), f)
            }
        }

        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            FromStr for [<$name $s:upper $b>]<RMIN, RMAX> {
            type Err = ParseIntError;
            #[inline]
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                Self::new([<$s:lower $b>]::from_str(s)?).ok_or_else(||"".parse::<i32>().unwrap_err())
            }
        }

        /* conversions */

        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            From<[<$name $s:upper $b>]<RMIN, RMAX>> for [<$s:lower $b>] {
            #[inline]
            #[must_use]
            fn from(value: [<$name $s:upper $b>]<RMIN, RMAX>) -> [<$s:lower $b>] {
                value.get()
            }
        }

        impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            TryFrom<[<$s:lower $b>]> for [<$name $s:upper $b>]<RMIN, RMAX> {
            type Error = core::num::TryFromIntError;

            #[inline]
            fn try_from(value: [<$s:lower $b>]) -> Result<Self, Self::Error> {
                // We generate a TryFromIntError by intentionally causing a failed conversion.
                #[cfg(not(feature = "unsafe_num"))]
                return Self::new(value).ok_or_else(|| i8::try_from(255_u8).unwrap_err());
                #[cfg(feature = "unsafe_num")]
                return Self::new(value)
                    .ok_or_else(|| unsafe { i8::try_from(255_u8).unwrap_err_unchecked() });
            }
        }

        /* external impls*/


        #[cfg(all(feature = "bytemuck", feature = "unsafe_num"))]
        #[cfg_attr(feature = "nightly", doc(cfg(all(feature = "bytemuck", feature = "unsafe_num"))))]
        unsafe impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            ZeroableInOption for [<$name $s:upper $b>]<RMIN, RMAX> {}

        #[cfg(all(feature = "bytemuck", feature = "unsafe_num"))]
        #[cfg_attr(feature = "nightly", doc(cfg(all(feature = "bytemuck", feature = "unsafe_num"))))]
        unsafe impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            PodInOption for [<$name $s:upper $b>]<RMIN, RMAX> {}

        #[cfg(all(feature = "bytemuck", feature = "unsafe_num"))]
        #[cfg_attr(feature = "nightly", doc(cfg(all(feature = "bytemuck", feature = "unsafe_num"))))]
        unsafe impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            NoUninit for [<$name $s:upper $b>]<RMIN, RMAX> {}

        #[cfg(all(feature = "bytemuck", feature = "unsafe_num"))]
        #[cfg_attr(feature = "nightly", doc(cfg(all(feature = "bytemuck", feature = "unsafe_num"))))]
        unsafe impl<const RMIN: [<$s:lower $b>], const RMAX: [<$s:lower $b>]>
            CheckedBitPattern for [<$name $s:upper $b>]<RMIN, RMAX> {
            type Bits = [<$s:lower $b>];

            #[inline]
            fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
                // if RMAX bit representation wraps around its type's MAX value
                // (e.g between 0_i8 and i8::MAX or - i8::MIN and Self::RMAX_BITS):
                if Self::RMAX_BITS < Self::RMIN_BITS {
                    !(*bits >= Self::RMIN_BITS || *bits <= Self::RMAX_BITS)
                // if it doesn't (e.g. between 0_i8 and Self::RMAX_BITS):
                } else {
                    !(*bits >= Self::RMIN_BITS && *bits <= Self::RMAX_BITS)
                }
            }
        }
    }};
}
use impl_non_range;