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" `", stringify!($name), r"` provides ergonomics to handle user provided 1-baed index safely.")]
52 #[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 one-based 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]
130 pub const fn from_one_based_nonzero(v: $nonzerotype) -> Self {
131 Self(v)
132 }
133
134 #[inline]
138 pub const fn from_zero_based(v: $itype) -> Option<Self> {
139 if v == <$nonzerotype>::MAX.get() {
140 return None;
141 }
142 Some($name(unsafe { <$nonzerotype>::new_unchecked(v + 1) }))
144 }
145
146 #[doc = concat!(r" This function results in undefined behavior when `v == ", stringify!($itype), r"::MAX`.")]
150 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
152 #[doc = concat!(r" ", stringify!($name), "::from_zero_based_unchecked(", stringify!($itype), r"::MAX);")]
155 #[inline]
158 pub const unsafe fn from_zero_based_unchecked(v: $itype) -> Self {
159 $name(unsafe { <$nonzerotype>::new_unchecked(v + 1) })
161 }
162
163 pub const fn as_zero_based(self) -> $itype {
165 self.0.get() - 1
166 }
167
168 pub const fn as_one_based(self) -> $nonzerotype {
170 self.0
171 }
172
173 #[doc = r" ```"]
176 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
177 #[doc = concat!(r" let one = ", stringify!($name), "::from_zero_based(1).unwrap();")]
178 #[doc = concat!(r" let two = ", stringify!($name), "::from_zero_based(2).unwrap();")]
179 #[doc = concat!(r" let max = ", stringify!($name), "::MAX;")]
180 #[doc = r""]
181 #[doc = r" assert_eq!(Some(two), one.checked_add(1));"]
182 #[doc = r" assert_eq!(None, max.checked_add(1));"]
183 #[doc = r" ```"]
184 pub const fn checked_add(self, other: $itype) -> Option<Self> {
185 match self.0.checked_add(other) {
186 None => None,
187 Some(v) => Some(Self(v)),
188 }
189 }
190
191 #[doc = r" ```"]
194 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
195 #[doc = concat!(r" let one = ", stringify!($name), "::from_zero_based(1).unwrap();")]
196 #[doc = concat!(r" let two = ", stringify!($name), "::from_zero_based(2).unwrap();")]
197 #[doc = concat!(r" let max = ", stringify!($name), "::MAX;")]
198 #[doc = r""]
199 #[doc = r" assert_eq!(two, one.saturating_add(1));"]
200 #[doc = r" assert_eq!(max, max.saturating_add(1));"]
201 #[doc = r" ```"]
202 pub const fn saturating_add(self, other: $itype) -> Self {
203 Self(self.0.saturating_add(other))
204 }
205 }
206 };
207}
208
209define_one_based!(OneBasedU8, u8, NonZeroU8);
210define_one_based!(OneBasedU16, u16, NonZeroU16);
211define_one_based!(OneBasedU32, u32, NonZeroU32);
212define_one_based!(OneBasedU64, u64, NonZeroU64);
213define_one_based!(OneBasedU128, u128, NonZeroU128);
214define_one_based!(OneBasedUsize, usize, NonZeroUsize);
215
216macro_rules! impl_from_one_based {
217 ($source:ty => $($target:ty),+) => {$(
218 impl core::convert::From<$source> for $target {
219 #[doc = concat!(r"Converts [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
220 #[inline]
221 fn from(value: $source) -> Self {
222 use core::convert::Into as _;
223 let v: <$target as OneBased>::NonZeroType = value.as_one_based().into();
224 <$target>::from_one_based_nonzero(v)
225 }
226 }
227 )*};
228}
229
230impl_from_one_based!(OneBasedU8 => OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);
231impl_from_one_based!(OneBasedU16 => OneBasedU32, OneBasedU64, OneBasedU128);
232impl_from_one_based!(OneBasedU32 => OneBasedU64, OneBasedU128);
233impl_from_one_based!(OneBasedU64 => OneBasedU128);
234
235macro_rules! impl_try_from_one_based {
236 ($source:ty => $($target:ty),+) => {$(
237 impl core::convert::TryFrom<$source> for $target {
238 type Error = core::num::TryFromIntError;
239
240 #[doc = concat!(r"Attempts to convert [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
241 #[inline]
242 fn try_from(value: $source) -> Result<Self, Self::Error> {
243 use core::convert::TryInto as _;
244 let v: <$target as OneBased>::NonZeroType = value.as_one_based().try_into()?;
245 Ok(<$target>::from_one_based_nonzero(v))
246 }
247 }
248 )*};
249}
250
251impl_try_from_one_based!(OneBasedU8 => OneBasedUsize);
252impl_try_from_one_based!(OneBasedU16 => OneBasedUsize, OneBasedU8);
253impl_try_from_one_based!(OneBasedU32 => OneBasedUsize, OneBasedU8, OneBasedU16);
254impl_try_from_one_based!(OneBasedU64 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32);
255impl_try_from_one_based!(OneBasedU128 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64);
256impl_try_from_one_based!(OneBasedUsize => OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);