1#![no_std]
30
31use core::{
32 fmt::Display,
33 num::{
34 NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, ParseIntError,
35 },
36 str::FromStr,
37};
38
39trait OneBased {
40 type IntType;
41 type NonZeroType;
42}
43
44macro_rules! define_one_based {
45 ($name:ident, $itype:ty, $nonzerotype:ty) => {
46 #[doc = concat!(r" Represents 1-based index of [`", stringify!($itype), r"`].")]
47 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
55 #[doc = r" // Creates from 1-based index"]
56 #[doc = concat!(r" let v = ", stringify!($name),r"::from_one_based(5).unwrap();")]
57 #[doc = r" assert_eq!(v.as_zero_based(), 4);"]
58 #[doc = r""]
59 #[doc = r" // Creates from 0-based index"]
60 #[doc = concat!(r" let v = ", stringify!($name),r"::from_zero_based(0).unwrap();")]
61 #[doc = r" assert_eq!(v.as_one_based().get(), 1);"]
62 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
64 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65 pub struct $name($nonzerotype);
66
67 impl OneBased for $name {
68 type IntType = $itype;
69 type NonZeroType = $nonzerotype;
70 }
71
72 impl Display for $name {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 self.as_one_based().fmt(f)
75 }
76 }
77
78 impl FromStr for $name {
79 type Err = ParseIntError;
80
81 fn from_str(s: &str) -> Result<Self, Self::Err> {
82 let v: $nonzerotype = s.parse()?;
83 Ok(Self::from_one_based_nonzero(v))
84 }
85 }
86
87 impl $name {
88 #[doc = concat!(r" This value is equal to ", stringify!($itype), r"::BITS.")]
91 pub const BITS: u32 = <$itype>::BITS;
92
93 pub const MIN: Self = Self::from_one_based_nonzero(<$nonzerotype>::MIN);
95
96 #[doc = concat!(r" The largest value that can be represented by this non-zero integer type, equal to `from_one_based(", stringify!($itype), r"::MAX)`.")]
97 pub const MAX: Self = Self::from_one_based_nonzero(<$nonzerotype>::MAX);
98
99 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
105 #[doc = concat!(r" const ONE_BASED_TEN: ", stringify!($name), r" = ", stringify!($name), r#"::from_one_based(10).expect("10 is non zero");"#)]
106 #[inline]
109 pub const fn from_one_based(v: $itype) -> Option<Self> {
110 match <$nonzerotype>::new(v) {
111 None => None,
112 Some(v) => Some($name(v)),
113 }
114 }
115
116 #[inline]
122 pub const unsafe fn from_one_based_unchecked(v: $itype) -> Self {
123 $name(<$nonzerotype>::new_unchecked(v))
124 }
125
126 #[inline]
129 pub const fn from_one_based_nonzero(v: $nonzerotype) -> Self {
130 Self(v)
131 }
132
133 #[inline]
137 pub const fn from_zero_based(v: $itype) -> Option<Self> {
138 if v == <$nonzerotype>::MAX.get() {
139 return None;
140 }
141 Some($name(unsafe { <$nonzerotype>::new_unchecked(v + 1) }))
143 }
144
145 #[doc = concat!(r" This function results in undefined behavior when `v == ", stringify!($itype), r"::MAX`.")]
149 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
151 #[doc = concat!(r" ", stringify!($name), "::from_zero_based_unchecked(", stringify!($itype), r"::MAX);")]
154 #[inline]
157 pub const unsafe fn from_zero_based_unchecked(v: $itype) -> Self {
158 $name(unsafe { <$nonzerotype>::new_unchecked(v + 1) })
160 }
161
162 pub const fn as_zero_based(self) -> $itype {
164 self.0.get() - 1
165 }
166
167 pub const fn as_one_based(self) -> $nonzerotype {
169 self.0
170 }
171
172 #[doc = r" ```"]
175 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
176 #[doc = concat!(r" let one = ", stringify!($name), "::from_zero_based(1).unwrap();")]
177 #[doc = concat!(r" let two = ", stringify!($name), "::from_zero_based(2).unwrap();")]
178 #[doc = concat!(r" let max = ", stringify!($name), "::MAX;")]
179 #[doc = r""]
180 #[doc = r" assert_eq!(Some(two), one.checked_add(1));"]
181 #[doc = r" assert_eq!(None, max.checked_add(1));"]
182 #[doc = r" ```"]
183 pub const fn checked_add(self, other: $itype) -> Option<Self> {
184 match self.0.checked_add(other) {
185 None => None,
186 Some(v) => Some(Self(v)),
187 }
188 }
189
190 #[doc = r" ```"]
193 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
194 #[doc = concat!(r" let one = ", stringify!($name), "::from_zero_based(1).unwrap();")]
195 #[doc = concat!(r" let two = ", stringify!($name), "::from_zero_based(2).unwrap();")]
196 #[doc = concat!(r" let max = ", stringify!($name), "::MAX;")]
197 #[doc = r""]
198 #[doc = r" assert_eq!(two, one.saturating_add(1));"]
199 #[doc = r" assert_eq!(max, max.saturating_add(1));"]
200 #[doc = r" ```"]
201 pub const fn saturating_add(self, other: $itype) -> Self {
202 Self(self.0.saturating_add(other))
203 }
204 }
205 };
206}
207
208define_one_based!(OneBasedU8, u8, NonZeroU8);
209define_one_based!(OneBasedU16, u16, NonZeroU16);
210define_one_based!(OneBasedU32, u32, NonZeroU32);
211define_one_based!(OneBasedU64, u64, NonZeroU64);
212define_one_based!(OneBasedU128, u128, NonZeroU128);
213define_one_based!(OneBasedUsize, usize, NonZeroUsize);
214
215macro_rules! impl_from_one_based {
216 ($source:ty => $($target:ty),+) => {$(
217 impl core::convert::From<$source> for $target {
218 #[doc = concat!(r"Converts [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
219 #[inline]
220 fn from(value: $source) -> Self {
221 use core::convert::Into as _;
222 let v: <$target as OneBased>::NonZeroType = value.as_one_based().into();
223 <$target>::from_one_based_nonzero(v)
224 }
225 }
226 )*};
227}
228
229impl_from_one_based!(OneBasedU8 => OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);
230impl_from_one_based!(OneBasedU16 => OneBasedU32, OneBasedU64, OneBasedU128);
231impl_from_one_based!(OneBasedU32 => OneBasedU64, OneBasedU128);
232impl_from_one_based!(OneBasedU64 => OneBasedU128);
233
234macro_rules! impl_try_from_one_based {
235 ($source:ty => $($target:ty),+) => {$(
236 impl core::convert::TryFrom<$source> for $target {
237 type Error = core::num::TryFromIntError;
238
239 #[doc = concat!(r"Attempts to convert [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
240 #[inline]
241 fn try_from(value: $source) -> Result<Self, Self::Error> {
242 use core::convert::TryInto as _;
243 let v: <$target as OneBased>::NonZeroType = value.as_one_based().try_into()?;
244 Ok(<$target>::from_one_based_nonzero(v))
245 }
246 }
247 )*};
248}
249
250impl_try_from_one_based!(OneBasedU8 => OneBasedUsize);
251impl_try_from_one_based!(OneBasedU16 => OneBasedUsize, OneBasedU8);
252impl_try_from_one_based!(OneBasedU32 => OneBasedUsize, OneBasedU8, OneBasedU16);
253impl_try_from_one_based!(OneBasedU64 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32);
254impl_try_from_one_based!(OneBasedU128 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64);
255impl_try_from_one_based!(OneBasedUsize => OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);