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" # use one_based::", stringify!($name), r";")]
94 #[doc = concat!(r" const ONE_BASED_TEN: ", stringify!($name), r" = ", stringify!($name), r#"::from_one_based(10).expect("10 is non zero");"#)]
95 #[inline]
98 pub const fn from_one_based(v: $itype) -> Option<Self> {
99 match <$nonzerotype>::new(v) {
100 None => None,
101 Some(v) => Some($name(v)),
102 }
103 }
104
105 #[inline]
111 pub const unsafe fn from_one_based_unchecked(v: $itype) -> Self {
112 $name(<$nonzerotype>::new_unchecked(v))
113 }
114
115 #[inline]
118 pub const fn from_one_based_nonzero(v: $nonzerotype) -> Self {
119 Self(v)
120 }
121
122 #[inline]
126 pub const fn from_zero_based(v: $itype) -> Option<Self> {
127 if v == <$nonzerotype>::MAX.get() {
128 return None;
129 }
130 Some($name(unsafe { <$nonzerotype>::new_unchecked(v + 1) }))
132 }
133
134 #[doc = concat!(r" This function results in undefined behavior when `v == ", stringify!($itype), r"::MAX`.")]
138 #[doc = concat!(r" # use one_based::", stringify!($name), r";")]
140 #[doc = concat!(r" ", stringify!($name), "::from_zero_based_unchecked(", stringify!($itype), r"::MAX);")]
143 #[inline]
146 pub const unsafe fn from_zero_based_unchecked(v: $itype) -> Self {
147 $name(unsafe { <$nonzerotype>::new_unchecked(v + 1) })
149 }
150
151 pub const fn as_zero_based(&self) -> $itype {
153 self.0.get() - 1
154 }
155
156 pub const fn as_one_based(&self) -> $nonzerotype {
158 self.0
159 }
160 }
161 };
162}
163
164define_one_based!(OneBasedU8, u8, NonZeroU8);
165define_one_based!(OneBasedU16, u16, NonZeroU16);
166define_one_based!(OneBasedU32, u32, NonZeroU32);
167define_one_based!(OneBasedU64, u64, NonZeroU64);
168define_one_based!(OneBasedU128, u128, NonZeroU128);
169define_one_based!(OneBasedUsize, usize, NonZeroUsize);
170
171macro_rules! impl_from_one_based {
172 ($source:ty => $($target:ty),+) => {$(
173 impl core::convert::From<$source> for $target {
174 #[doc = concat!(r"Converts [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
175 #[inline]
176 fn from(value: $source) -> Self {
177 use core::convert::Into as _;
178 let v: <$target as OneBased>::NonZeroType = value.as_one_based().into();
179 <$target>::from_one_based_nonzero(v)
180 }
181 }
182 )*};
183}
184
185impl_from_one_based!(OneBasedU8 => OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);
186impl_from_one_based!(OneBasedU16 => OneBasedU32, OneBasedU64, OneBasedU128);
187impl_from_one_based!(OneBasedU32 => OneBasedU64, OneBasedU128);
188impl_from_one_based!(OneBasedU64 => OneBasedU128);
189
190macro_rules! impl_try_from_one_based {
191 ($source:ty => $($target:ty),+) => {$(
192 impl core::convert::TryFrom<$source> for $target {
193 type Error = core::num::TryFromIntError;
194
195 #[doc = concat!(r"Attempts to convert [`", stringify!($source), r"`] to [`", stringify!($target), r"`].")]
196 #[inline]
197 fn try_from(value: $source) -> Result<Self, Self::Error> {
198 use core::convert::TryInto as _;
199 let v: <$target as OneBased>::NonZeroType = value.as_one_based().try_into()?;
200 Ok(<$target>::from_one_based_nonzero(v))
201 }
202 }
203 )*};
204}
205
206impl_try_from_one_based!(OneBasedU8 => OneBasedUsize);
207impl_try_from_one_based!(OneBasedU16 => OneBasedUsize, OneBasedU8);
208impl_try_from_one_based!(OneBasedU32 => OneBasedUsize, OneBasedU8, OneBasedU16);
209impl_try_from_one_based!(OneBasedU64 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32);
210impl_try_from_one_based!(OneBasedU128 => OneBasedUsize, OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64);
211impl_try_from_one_based!(OneBasedUsize => OneBasedU8, OneBasedU16, OneBasedU32, OneBasedU64, OneBasedU128);