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 };
173}
174
175define_one_based!(OneBasedU8, u8, NonZeroU8);
176define_one_based!(OneBasedU16, u16, NonZeroU16);
177define_one_based!(OneBasedU32, u32, NonZeroU32);
178define_one_based!(OneBasedU64, u64, NonZeroU64);
179define_one_based!(OneBasedU128, u128, NonZeroU128);
180define_one_based!(OneBasedUsize, usize, NonZeroUsize);
181
182macro_rules! impl_from_one_based {
183 ($source:ty => $($target:ty),+) => {$(
184 impl core::convert::From<$source> for $target {
185 #[doc = concat!(r"Converts [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
186 #[inline]
187 fn from(value: $source) -> Self {
188 use core::convert::Into as _;
189 let v: <$target as OneBased>::NonZeroType = value.as_one_based().into();
190 <$target>::from_one_based_nonzero(v)
191 }
192 }
193 )*};
194}
195
196impl_from_one_based!(OneBasedU8 => OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);
197impl_from_one_based!(OneBasedU16 => OneBasedU32, OneBasedU64, OneBasedU128);
198impl_from_one_based!(OneBasedU32 => OneBasedU64, OneBasedU128);
199impl_from_one_based!(OneBasedU64 => OneBasedU128);
200
201macro_rules! impl_try_from_one_based {
202 ($source:ty => $($target:ty),+) => {$(
203 impl core::convert::TryFrom<$source> for $target {
204 type Error = core::num::TryFromIntError;
205
206 #[doc = concat!(r"Attempts to convert [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
207 #[inline]
208 fn try_from(value: $source) -> Result<Self, Self::Error> {
209 use core::convert::TryInto as _;
210 let v: <$target as OneBased>::NonZeroType = value.as_one_based().try_into()?;
211 Ok(<$target>::from_one_based_nonzero(v))
212 }
213 }
214 )*};
215}
216
217impl_try_from_one_based!(OneBasedU8 => OneBasedUsize);
218impl_try_from_one_based!(OneBasedU16 => OneBasedUsize, OneBasedU8);
219impl_try_from_one_based!(OneBasedU32 => OneBasedUsize, OneBasedU8, OneBasedU16);
220impl_try_from_one_based!(OneBasedU64 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32);
221impl_try_from_one_based!(OneBasedU128 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64);
222impl_try_from_one_based!(OneBasedUsize => OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);