andex/
andex.rs

1// Copyright (C) 2021 Leandro Lisboa Penz <lpenz@lpenz.org>
2// This file is subject to the terms and conditions defined in
3// file 'LICENSE', which is part of this source code package.
4
5#![warn(missing_debug_implementations)]
6#![warn(missing_docs)]
7
8//! andex module
9//!
10//! andex code is structure in a way that allows users to copy this
11//! file to their projects and use andex as its own module, without a
12//! crate dependency.
13
14use core::any;
15use core::array;
16use core::cmp;
17use core::convert;
18use core::convert::TryFrom;
19use core::error;
20use core::fmt;
21use core::hash::{Hash, Hasher};
22use core::marker::PhantomData;
23use core::mem;
24use core::mem::MaybeUninit;
25use core::num;
26use core::ops;
27use core::slice;
28use core::str;
29
30/* Andex index type */
31
32/// Array index generic type
33///
34/// This generic type receives a user-specified "marker" type as the
35/// first type parameter to make it unique, and the size of the array
36/// as a second const generic `SIZE` parameter.
37///
38/// Note: the maximum numerical value in the andex is `SIZE - 1`.
39///
40/// Recommended usage, with an empty type as a marker to create a type
41/// alias:
42///
43/// ```
44/// use andex::*;
45///
46/// enum MyIdxMarker {}
47/// type MyIdx = Andex<MyIdxMarker, 12>;
48/// ```
49pub struct Andex<M, const SIZE: usize>(PhantomData<M>, usize);
50
51/// Andex-wide methods
52///
53/// [`Andex::new`] and [`Andex::iter`] are public, most other methods
54/// are only used in traits, and thus private.
55impl<M, const SIZE: usize> Andex<M, SIZE> {
56    /// The `SIZE` parameter, which is the size of the array that this
57    /// andex indexes.
58    pub const SIZE: usize = SIZE;
59
60    /// The first possible value.
61    pub const FIRST: Andex<M, SIZE> = Andex(PhantomData, 0);
62
63    /// The last possible value.
64    pub const LAST: Andex<M, SIZE> = Andex(PhantomData, SIZE - 1);
65
66    /// Create a new andex instance
67    ///
68    /// We recomment using this method in `const` contexts, passing
69    /// the index as a const generic function parameter. That allows
70    /// the compiler to check the index against the array bounds at
71    /// compile time.
72    ///
73    /// For instance, the following compiles:
74    /// ```
75    /// use andex::*;
76    ///
77    /// struct MyIdxMarker;
78    /// type MyIdx = Andex<MyIdxMarker, 12>;
79    ///
80    /// const MYVALUE : MyIdx = MyIdx::new::<0>();
81    /// ```
82    ///
83    /// While the following doesn't:
84    /// ```compile_fail
85    /// use andex::*;
86    ///
87    /// struct MyIdxMarker;
88    /// type MyIdx = Andex<MyIdxMarker, 13>;
89    ///
90    /// const MYVALUE : MyIdx = MyIdx::new::<15>();
91    /// ```
92    #[inline]
93    pub const fn new<const N: usize>() -> Self {
94        // Trick for compile-time check of N:
95        const ASSERT: [(); 1] = [(); 1];
96        #[allow(clippy::no_effect)]
97        ASSERT[(N >= SIZE) as usize];
98        Andex(PhantomData, N)
99    }
100
101    /// Returns the pair of the provided Andex.
102    ///
103    /// The "pair" is the element that is at the same distance from
104    /// the center. This definition is useful in some contexts. For
105    /// instance, the pair of [`Self::FIRST`] is [`Self::LAST`].
106    #[inline]
107    pub const fn pair(self) -> Self {
108        Andex(PhantomData, SIZE - self.1 - 1)
109    }
110
111    /// Return the next Andex in sequence, or None if it's the last one.
112    #[inline]
113    pub fn next(self) -> Option<Self> {
114        let i = usize::from(self);
115        if i < SIZE - 1 {
116            Some(Andex(PhantomData, i + 1))
117        } else {
118            None
119        }
120    }
121
122    /// Indexes the provided array
123    ///
124    /// Used internally by the `Index` trait implementation.
125    #[inline]
126    fn index_arr<'a, T>(&self, arr: &'a [T]) -> &'a T {
127        unsafe { arr.get_unchecked(usize::from(self)) }
128    }
129
130    /// Mut-indexes the provided array
131    ///
132    /// Used internally by the `IndexMut` trait implementation.
133    #[inline]
134    fn index_arr_mut<'a, T>(&self, arr: &'a mut [T]) -> &'a mut T {
135        unsafe { arr.get_unchecked_mut(usize::from(self)) }
136    }
137
138    /// Iterate all possible values of the index
139    ///
140    /// Useful to loop over an array inside a `struct`, without
141    /// holding a reference to the whole struct in the loop.
142    ///
143    /// # Example
144    ///
145    /// This prints all numbers from 0 to 11:
146    ///
147    /// ```
148    /// use andex::*;
149    ///
150    /// pub struct PlayerIdMarker;
151    /// type PlayerId = Andex<PlayerIdMarker, 12>;
152    ///
153    /// for i in PlayerId::iter() {
154    ///     println!("{}", i);
155    /// }
156    /// ```
157    pub fn iter() -> AndexIterator<M, SIZE> {
158        AndexIterator::<M, SIZE>::default()
159    }
160}
161
162/* Generic implementations
163 * We can't use the automatic derives to avoid requiring them in the
164 * Marker.
165 */
166
167impl<M, const SIZE: usize> Clone for Andex<M, SIZE> {
168    fn clone(&self) -> Self {
169        *self
170    }
171}
172
173impl<M, const SIZE: usize> Copy for Andex<M, SIZE> {}
174
175impl<M, const SIZE: usize> Hash for Andex<M, SIZE> {
176    fn hash<H: Hasher>(&self, state: &mut H) {
177        self.1.hash(state);
178    }
179}
180
181impl<M, const SIZE: usize> Default for Andex<M, SIZE> {
182    fn default() -> Self {
183        Andex(PhantomData, 0)
184    }
185}
186
187impl<M, const SIZE: usize> PartialEq for Andex<M, SIZE> {
188    fn eq(&self, other: &Self) -> bool {
189        self.1 == other.1
190    }
191}
192
193impl<M, const SIZE: usize> Eq for Andex<M, SIZE> {}
194
195impl<M, const SIZE: usize> PartialOrd for Andex<M, SIZE> {
196    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
197        Some(self.cmp(other))
198    }
199}
200
201impl<M, const SIZE: usize> Ord for Andex<M, SIZE> {
202    fn cmp(&self, other: &Self) -> cmp::Ordering {
203        self.1.cmp(&other.1)
204    }
205}
206
207impl<M, const SIZE: usize> From<Andex<M, SIZE>> for usize {
208    fn from(andex: Andex<M, SIZE>) -> Self {
209        andex.1
210    }
211}
212
213impl<M, const SIZE: usize> From<&Andex<M, SIZE>> for usize {
214    fn from(andex: &Andex<M, SIZE>) -> Self {
215        andex.1
216    }
217}
218
219impl<M, const SIZE: usize> convert::TryFrom<usize> for Andex<M, SIZE> {
220    type Error = Error;
221    fn try_from(value: usize) -> Result<Self, Self::Error> {
222        if value < SIZE {
223            Ok(Andex(PhantomData, value))
224        } else {
225            Err(Error::OutOfBounds { value, size: SIZE })
226        }
227    }
228}
229
230impl<M, const SIZE: usize> fmt::Debug for Andex<M, SIZE> {
231    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
232        write!(f, "{:?}", usize::from(self))
233    }
234}
235
236impl<M, const SIZE: usize> fmt::Display for Andex<M, SIZE> {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        write!(f, "{}", usize::from(self))
239    }
240}
241
242impl<M, const SIZE: usize> str::FromStr for Andex<M, SIZE> {
243    type Err = Error;
244
245    fn from_str(s: &str) -> Result<Self, Self::Err> {
246        Self::try_from(usize::from_str(s)?)
247    }
248}
249
250/* Iterator */
251
252/// Iterator for Andex instances
253///
254/// This is the type returned by Andex::<_,_>::iter().
255/// There's no reason to use it directly.
256///
257/// Iterating example:
258///
259/// ```
260/// use andex::*;
261///
262/// pub struct PlayerIdMarker;
263/// type PlayerId = Andex<PlayerIdMarker, 12>;
264///
265/// for i in PlayerId::iter() {
266///     println!("{}", i);
267/// }
268/// ```
269pub struct AndexIterator<M, const SIZE: usize>(Option<Andex<M, SIZE>>);
270
271impl<M, const SIZE: usize> fmt::Debug for AndexIterator<M, SIZE> {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        write!(f, "AndexIterator({:?})", self.0)
274    }
275}
276
277impl<M, const SIZE: usize> Default for AndexIterator<M, SIZE> {
278    fn default() -> Self {
279        AndexIterator(Some(Andex::<M, SIZE>::default()))
280    }
281}
282
283impl<M, const SIZE: usize> Iterator for AndexIterator<M, SIZE> {
284    type Item = Andex<M, SIZE>;
285    fn next(&mut self) -> Option<Self::Item> {
286        if let Some(i) = self.0.take() {
287            self.0 = i.next();
288            Some(i)
289        } else {
290            None
291        }
292    }
293}
294
295/* Array wrapper */
296
297/// Array wrapper indexable by the provided Andex type.
298///
299/// Example:
300///
301/// ```
302/// use andex::*;
303///
304/// enum MyIdxMarker {}
305/// type MyIdx = Andex<MyIdxMarker, 12>;
306///
307/// // Create the array wrapper:
308/// type MyU32 = AndexableArray<MyIdx, u32, { MyIdx::SIZE }>;
309///
310/// // We can create other arrays with the same Andex type:
311/// type MyF64 = AndexableArray<MyIdx, f64, { MyIdx::SIZE }>;
312///
313/// // Create a default array:
314/// let myu32 = MyU32::default();
315/// // Print the first element:
316/// const first : MyIdx = MyIdx::new::<0>();
317/// println!("{:?}", myu32[first]);
318/// // Iterate and print all elements:
319/// for i in MyIdx::iter() {
320///     println!("{:?}", myu32[i]);
321/// }
322/// // Print the whole array
323/// println!("{:?}", myu32);
324/// ```
325#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
326pub struct AndexableArray<A, Item, const SIZE: usize>(PhantomData<A>, [Item; SIZE]);
327
328impl<A, Item: fmt::Debug, const SIZE: usize> fmt::Debug for AndexableArray<A, Item, SIZE> {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        write!(
331            f,
332            "AndexableArray<{}>({:?})",
333            any::type_name::<Item>(),
334            self.1
335        )
336    }
337}
338
339/// Helper macro that creates an AndexableArray from an Andex
340///
341/// This macro just uses the Andex argument to figure out the array
342/// size, so that we don't have to repeat it here.
343///
344/// Example:
345/// ```
346/// use andex::*;
347///
348/// enum MyIdxMarker {};
349/// type MyIdx = Andex<MyIdxMarker, 12>;
350///
351/// // Create the array wrapper with the macro:
352/// type MyU32 = andex_array!(MyIdx, u32);
353/// ```
354#[macro_export]
355macro_rules! andex_array {
356    ($andex: ty, $item: ty) => {
357        $crate::AndexableArray<$andex, $item, { <$andex>::SIZE }>
358    };
359}
360
361impl<A, Item, const SIZE: usize> AndexableArray<A, Item, SIZE> {
362    /// Returns an iterator over the `&AndexableArray`.
363    pub fn iter(&self) -> impl Iterator<Item = &Item> {
364        self.1.iter()
365    }
366}
367
368impl<A, Item: Copy, const SIZE: usize> Clone for AndexableArray<A, Item, SIZE> {
369    fn clone(&self) -> Self {
370        *self
371    }
372}
373
374impl<A, Item: Copy, const SIZE: usize> Copy for AndexableArray<A, Item, SIZE> {}
375
376impl<A, Item: Default + Copy, const SIZE: usize> Default for AndexableArray<A, Item, SIZE> {
377    fn default() -> Self {
378        AndexableArray(Default::default(), [Default::default(); SIZE])
379    }
380}
381
382impl<A, Item, const SIZE: usize> ops::Index<Andex<A, SIZE>>
383    for AndexableArray<Andex<A, SIZE>, Item, SIZE>
384{
385    type Output = Item;
386    fn index(&self, index: Andex<A, SIZE>) -> &Self::Output {
387        index.index_arr(&self.1)
388    }
389}
390
391impl<A, Item, const SIZE: usize> ops::IndexMut<Andex<A, SIZE>>
392    for AndexableArray<Andex<A, SIZE>, Item, SIZE>
393{
394    fn index_mut(&mut self, index: Andex<A, SIZE>) -> &mut Item {
395        index.index_arr_mut(&mut self.1)
396    }
397}
398
399impl<A, Item, const SIZE: usize> ops::Index<&Andex<A, SIZE>>
400    for AndexableArray<Andex<A, SIZE>, Item, SIZE>
401{
402    type Output = Item;
403    fn index(&self, index: &Andex<A, SIZE>) -> &Self::Output {
404        index.index_arr(&self.1)
405    }
406}
407
408impl<A, Item, const SIZE: usize> ops::IndexMut<&Andex<A, SIZE>>
409    for AndexableArray<Andex<A, SIZE>, Item, SIZE>
410{
411    fn index_mut(&mut self, index: &Andex<A, SIZE>) -> &mut Item {
412        index.index_arr_mut(&mut self.1)
413    }
414}
415
416impl<A, Item, const SIZE: usize> convert::AsRef<[Item; SIZE]> for AndexableArray<A, Item, SIZE> {
417    fn as_ref(&self) -> &[Item; SIZE] {
418        &self.1
419    }
420}
421
422impl<A, Item, const SIZE: usize> convert::AsMut<[Item; SIZE]> for AndexableArray<A, Item, SIZE> {
423    fn as_mut(&mut self) -> &mut [Item; SIZE] {
424        &mut self.1
425    }
426}
427
428impl<A, Item, const SIZE: usize> From<[Item; SIZE]> for AndexableArray<A, Item, SIZE> {
429    fn from(array: [Item; SIZE]) -> Self {
430        Self(PhantomData, array)
431    }
432}
433
434impl<A, Item, const SIZE: usize> From<&[Item; SIZE]> for AndexableArray<A, Item, SIZE>
435where
436    Item: Copy,
437{
438    fn from(array: &[Item; SIZE]) -> Self {
439        Self(PhantomData, *array)
440    }
441}
442
443impl<A, Item, const SIZE: usize> From<AndexableArray<A, Item, SIZE>> for [Item; SIZE]
444where
445    Item: Copy,
446{
447    fn from(andexable_array: AndexableArray<A, Item, SIZE>) -> [Item; SIZE] {
448        andexable_array.1
449    }
450}
451
452impl<A, Item, const SIZE: usize> From<&AndexableArray<A, Item, SIZE>> for [Item; SIZE]
453where
454    Item: Copy,
455{
456    fn from(andexable_array: &AndexableArray<A, Item, SIZE>) -> [Item; SIZE] {
457        andexable_array.1
458    }
459}
460
461impl<A, Item, const SIZE: usize> IntoIterator for AndexableArray<A, Item, SIZE> {
462    type Item = Item;
463    type IntoIter = array::IntoIter<Item, SIZE>;
464    fn into_iter(self) -> Self::IntoIter {
465        IntoIterator::into_iter(self.1)
466    }
467}
468
469impl<'a, A, Item, const SIZE: usize> IntoIterator for &'a AndexableArray<A, Item, SIZE> {
470    type Item = &'a Item;
471    type IntoIter = slice::Iter<'a, Item>;
472    fn into_iter(self) -> Self::IntoIter {
473        self.1.iter()
474    }
475}
476
477impl<'a, A, Item, const SIZE: usize> IntoIterator for &'a mut AndexableArray<A, Item, SIZE> {
478    type Item = &'a mut Item;
479    type IntoIter = slice::IterMut<'a, Item>;
480    fn into_iter(self) -> Self::IntoIter {
481        self.1.iter_mut()
482    }
483}
484
485impl<A, Item, const SIZE: usize> core::iter::FromIterator<Item> for AndexableArray<A, Item, SIZE> {
486    fn from_iter<I: core::iter::IntoIterator<Item = Item>>(intoiter: I) -> Self {
487        let mut andexable = AndexableArray::<A, MaybeUninit<Item>, SIZE>(PhantomData, unsafe {
488            mem::MaybeUninit::uninit().assume_init()
489        });
490        let mut iter = intoiter.into_iter();
491        for item in &mut andexable {
492            if let Some(fromiter) = iter.next() {
493                item.write(fromiter);
494            } else {
495                panic!("iterator too short for andexable type");
496            }
497        }
498        if iter.next().is_some() {
499            panic!("iterator too long for andexable type");
500        }
501
502        unsafe { mem::transmute_copy::<_, AndexableArray<A, Item, SIZE>>(&andexable) }
503    }
504}
505
506impl<'a, A, Item: 'a + Copy, const SIZE: usize> core::iter::FromIterator<&'a Item>
507    for AndexableArray<A, Item, SIZE>
508{
509    fn from_iter<I: core::iter::IntoIterator<Item = &'a Item>>(intoiter: I) -> Self {
510        let mut andexable = AndexableArray::<A, MaybeUninit<Item>, SIZE>(PhantomData, unsafe {
511            mem::MaybeUninit::uninit().assume_init()
512        });
513        let mut iter = intoiter.into_iter();
514        for item in &mut andexable {
515            if let Some(&fromiter) = iter.next() {
516                item.write(fromiter);
517            } else {
518                panic!("iterator too short for andexable type");
519            }
520        }
521        if iter.next().is_some() {
522            panic!("iterator too long for andexable type");
523        }
524
525        unsafe { mem::transmute_copy::<_, AndexableArray<A, Item, SIZE>>(&andexable) }
526    }
527}
528
529/* Errors: */
530
531/// Andex errors enum
532///
533/// This is used by try_from when an invalid value is passed.
534///
535/// For instance, this code prints the error:
536///
537/// ```
538/// use core::convert::TryFrom;
539/// use andex::*;
540///
541/// enum MyIdxMarker {}
542/// type MyIdx = Andex<MyIdxMarker, 12>;
543///
544/// println!("{:?}", MyIdx::try_from(15_usize));
545/// ```
546#[derive(Debug, Clone)]
547pub enum Error {
548    /// Tried to use a out-of-bounds value to create an andex
549    OutOfBounds {
550        /// The out-of-bounds value that was provided at andex
551        /// creation
552        value: usize,
553        /// The `SIZE` of the andex type
554        ///
555        /// The maximum value accepted is `SIZE - 1`
556        size: usize,
557    },
558    /// Underlying ParseIntError from integer parsing
559    ParseIntError(num::ParseIntError),
560}
561
562impl error::Error for Error {}
563
564impl From<num::ParseIntError> for Error {
565    fn from(err: num::ParseIntError) -> Self {
566        Error::ParseIntError(err)
567    }
568}
569
570impl fmt::Display for Error {
571    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
572        match self {
573            Error::OutOfBounds { value, size } => write!(
574                f,
575                "value {} is out-of-bounds for index with size {}",
576                value, size
577            ),
578            Error::ParseIntError(err) => write!(f, "{}", err),
579        }
580    }
581}