Skip to main content

optional_numeric_index/
lib.rs

1#[cfg(test)]
2mod tests;
3
4#[macro_export]
5macro_rules! implement_generic_index {
6    ($index:ident, $optional_index:ident) => {
7        struct $index<IndexType>(IndexType);
8
9        struct $optional_index<IndexType>(IndexType);
10
11        implement_generic_index!($index, $optional_index, __inner__);
12    };
13
14    (pub $index:ident, pub $optional_index:ident) => {
15        pub struct $index<IndexType>(IndexType);
16
17        pub struct $optional_index<IndexType>(IndexType);
18
19        implement_generic_index!($index, $optional_index, __inner__);
20    };
21
22    (pub(crate) $index:ident, pub(crate) $optional_index:ident) => {
23        pub(crate) struct $index<IndexType>(IndexType);
24
25        pub(crate) struct $optional_index<IndexType>(IndexType);
26
27        implement_generic_index!($index, $optional_index, __inner__);
28    };
29
30    (pub(super) $index:ident, pub(super) $optional_index:ident) => {
31        pub(super) struct $index<IndexType>(IndexType);
32
33        pub(super) struct $optional_index<IndexType>(IndexType);
34
35        implement_generic_index!($index, $optional_index, __inner__);
36    };
37
38    (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident) => {
39        pub(in $index_visibility) struct $index<IndexType>(IndexType);
40
41        pub(in $optional_index_visibility) struct $optional_index<IndexType>(IndexType);
42
43        implement_generic_index!($index, $optional_index, __inner__);
44    };
45
46    ($index:ident, $optional_index:ident, __inner__) => {
47        impl<IndexType> $index<IndexType> {
48            #[allow(dead_code)]
49            pub fn new(value: IndexType) -> Self
50            where
51                IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
52            {
53                assert_ne!(value, IndexType::max_value());
54                Self(value)
55            }
56
57            #[allow(dead_code)]
58            pub fn from_usize(value: usize) -> Self
59            where
60                IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
61            {
62                Self::new(
63                    value
64                        .try_into()
65                        .ok()
66                        .expect("index conversion from usize failed"),
67                )
68            }
69
70            #[allow(dead_code)]
71            pub fn from_raw(value: IndexType) -> Self
72            where
73                IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
74            {
75                Self::new(value)
76            }
77
78            #[allow(dead_code)]
79            pub fn into_raw(self) -> IndexType {
80                self.0
81            }
82        }
83
84        impl<IndexType> $optional_index<IndexType> {
85            #[allow(dead_code)]
86            pub fn new_some(value: IndexType) -> Self
87            where
88                IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
89            {
90                assert_ne!(value, IndexType::max_value());
91                Self(value)
92            }
93
94            #[allow(dead_code)]
95            pub fn new_none() -> Self
96            where
97                IndexType: num_traits::bounds::UpperBounded,
98            {
99                Self(IndexType::max_value())
100            }
101
102            #[allow(dead_code)]
103            pub fn from_raw(value: Option<IndexType>) -> Self
104            where
105                IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
106            {
107                if let Some(value) = value {
108                    Self::new_some(value)
109                } else {
110                    Self::new_none()
111                }
112            }
113
114            #[allow(dead_code)]
115            pub fn into_raw(self) -> Option<IndexType>
116            where
117                IndexType: num_traits::bounds::UpperBounded + Eq,
118            {
119                if self.is_some() { Some(self.0) } else { None }
120            }
121
122            #[allow(dead_code)]
123            pub fn is_some(&self) -> bool
124            where
125                IndexType: num_traits::bounds::UpperBounded + Eq,
126            {
127                self.0 != IndexType::max_value()
128            }
129
130            #[allow(dead_code)]
131            pub fn is_none(&self) -> bool
132            where
133                IndexType: num_traits::bounds::UpperBounded + Eq,
134            {
135                self.0 == IndexType::max_value()
136            }
137        }
138
139        /////////////////////////
140        ////// Conversions //////
141        /////////////////////////
142
143        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
144            From<Option<$index<IndexType>>> for $optional_index<IndexType>
145        {
146            fn from(index: Option<$index<IndexType>>) -> Self {
147                if let Some(index) = index {
148                    Self::new_some(index.0)
149                } else {
150                    Self::new_none()
151                }
152            }
153        }
154
155        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
156            From<$index<IndexType>> for $optional_index<IndexType>
157        {
158            fn from(index: $index<IndexType>) -> Self {
159                Self::new_some(index.0)
160            }
161        }
162
163        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
164            From<$optional_index<IndexType>> for Option<$index<IndexType>>
165        {
166            fn from(optional_index: $optional_index<IndexType>) -> Self {
167                if optional_index.is_some() {
168                    Some($index::new(optional_index.0))
169                } else {
170                    None
171                }
172            }
173        }
174
175        ////////////////////////////////////
176        ////// Conversions with usize //////
177        ////////////////////////////////////
178
179        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
180            From<usize> for $index<IndexType>
181        {
182            fn from(value: usize) -> Self {
183                Self::new(
184                    value
185                        .try_into()
186                        .ok()
187                        .expect("index conversion from usize failed"),
188                )
189            }
190        }
191
192        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
193            From<usize> for $optional_index<IndexType>
194        {
195            fn from(value: usize) -> Self {
196                Self::new_some(
197                    value
198                        .try_into()
199                        .ok()
200                        .expect("index conversion from usize failed"),
201                )
202            }
203        }
204
205        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
206            From<Option<usize>> for $optional_index<IndexType>
207        {
208            fn from(value: Option<usize>) -> Self {
209                if let Some(value) = value {
210                    Self::new_some(
211                        value
212                            .try_into()
213                            .ok()
214                            .expect("index conversion from usize failed"),
215                    )
216                } else {
217                    Self::new_none()
218                }
219            }
220        }
221
222        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryInto<usize>>
223            From<$index<IndexType>> for usize
224        {
225            fn from(value: $index<IndexType>) -> Self {
226                value
227                    .0
228                    .try_into()
229                    .ok()
230                    .expect("index conversion from usize failed")
231            }
232        }
233
234        impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
235            From<$optional_index<IndexType>> for Option<usize>
236        {
237            fn from(index: $optional_index<IndexType>) -> Self {
238                if index.is_some() {
239                    Some(
240                        index
241                            .0
242                            .try_into()
243                            .ok()
244                            .expect("index conversion to usize failed"),
245                    )
246                } else {
247                    None
248                }
249            }
250        }
251
252        ////////////////////////
253        ////// Formatting //////
254        ////////////////////////
255
256        impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
257            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258                write!(f, "{}({:?})", stringify!($index), self.0)
259            }
260        }
261
262        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
263            for $optional_index<IndexType>
264        {
265            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
266                if self.is_some() {
267                    write!(f, "{}({:?})", stringify!($optional_index), self.0)
268                } else {
269                    write!(f, "{}(None)", stringify!($optional_index))
270                }
271            }
272        }
273
274        impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
275            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276                write!(f, "{}", self.0)
277            }
278        }
279
280        impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
281            for $optional_index<IndexType>
282        {
283            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284                if self.is_some() {
285                    write!(f, "{}", self.0)
286                } else {
287                    write!(f, "None")
288                }
289            }
290        }
291
292        //////////////////////////
293        ////// Clone + Copy //////
294        //////////////////////////
295
296        impl<IndexType: Clone> Clone for $index<IndexType> {
297            fn clone(&self) -> Self {
298                Self(self.0.clone())
299            }
300        }
301
302        impl<IndexType: Clone> Clone for $optional_index<IndexType> {
303            fn clone(&self) -> Self {
304                Self(self.0.clone())
305            }
306        }
307
308        impl<IndexType: Copy> Copy for $index<IndexType> {}
309
310        impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
311
312        //////////////////////
313        ////// Equality //////
314        //////////////////////
315
316        impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
317            fn eq(&self, other: &Self) -> bool {
318                self.0.eq(&other.0)
319            }
320        }
321
322        impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
323            fn eq(&self, other: &Self) -> bool {
324                self.0.eq(&other.0)
325            }
326        }
327
328        impl<IndexType: Eq> Eq for $index<IndexType> {}
329
330        impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
331
332        //////////////////////
333        ////// Ordering //////
334        //////////////////////
335
336        impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
337            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
338                self.0.partial_cmp(&other.0)
339            }
340        }
341
342        impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
343            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
344                self.0.partial_cmp(&other.0)
345            }
346        }
347
348        impl<IndexType: Ord> Ord for $index<IndexType> {
349            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
350                self.0.cmp(&other.0)
351            }
352        }
353
354        impl<IndexType: Ord> Ord for $optional_index<IndexType> {
355            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
356                self.0.cmp(&other.0)
357            }
358        }
359
360        /////////////////////
361        ////// Hashing //////
362        /////////////////////
363
364        impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
365            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
366                self.0.hash(state);
367            }
368        }
369
370        impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
371            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
372                self.0.hash(state);
373            }
374        }
375    };
376}
377
378#[macro_export]
379macro_rules! implement_fixed_index {
380    ($index:ident, $optional_index:ident, $index_type:ty) => {
381        struct $index($index_type);
382
383        struct $optional_index($index_type);
384
385        implement_fixed_index!($index, $optional_index, $index_type, __inner__);
386    };
387
388    (pub $index:ident, pub $optional_index:ident, $index_type:ty) => {
389        pub struct $index($index_type);
390
391        pub struct $optional_index($index_type);
392
393        implement_fixed_index!($index, $optional_index, $index_type, __inner__);
394    };
395
396    (pub(crate) $index:ident, pub(crate) $optional_index:ident, $index_type:ty) => {
397        pub(crate) struct $index($index_type);
398
399        pub(crate) struct $optional_index($index_type);
400
401        implement_fixed_index!($index, $optional_index, $index_type, __inner__);
402    };
403
404    (pub(super) $index:ident, pub(super) $optional_index:ident, $index_type:ty) => {
405        pub(super) struct $index($index_type);
406
407        pub(super) struct $optional_index($index_type);
408
409        implement_fixed_index!($index, $optional_index, $index_type, __inner__);
410    };
411
412    (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident, $index_type:ty) => {
413        pub(in $index_visibility) struct $index($index_type);
414
415        pub(in $optional_index_visibility) struct $optional_index($index_type);
416
417        implement_fixed_index!($index, $optional_index, $index_type, __inner__);
418    };
419
420    ($index:ident, $optional_index:ident, $index_type:ty, __inner__) => {
421        impl $index {
422            #[allow(dead_code)]
423            pub fn new(value: $index_type) -> Self {
424                assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
425                Self(value)
426            }
427
428            #[allow(dead_code)]
429            pub fn from_raw(value: $index_type) -> Self {
430                Self::new(value)
431            }
432
433            #[allow(dead_code)]
434            pub fn into_raw(self) -> $index_type {
435                self.0
436            }
437        }
438
439        impl $optional_index {
440            #[allow(dead_code)]
441            pub fn new_some(value: $index_type) -> Self {
442                assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
443                Self(value)
444            }
445
446            #[allow(dead_code)]
447            pub fn new_none() -> Self {
448                Self(num_traits::bounds::UpperBounded::max_value())
449            }
450
451            #[allow(dead_code)]
452            pub fn from_raw(value: Option<$index_type>) -> Self {
453                if let Some(value) = value {
454                    Self::new_some(value)
455                } else {
456                    Self::new_none()
457                }
458            }
459
460            #[allow(dead_code)]
461            pub fn into_raw(self) -> Option<$index_type> {
462                if self.is_some() { Some(self.0) } else { None }
463            }
464
465            #[allow(dead_code)]
466            pub fn is_some(&self) -> bool {
467                self.0 != num_traits::bounds::UpperBounded::max_value()
468            }
469
470            #[allow(dead_code)]
471            pub fn is_none(&self) -> bool {
472                self.0 == num_traits::bounds::UpperBounded::max_value()
473            }
474        }
475
476        /////////////////////////
477        ////// Conversions //////
478        /////////////////////////
479
480        impl From<Option<$index>> for $optional_index {
481            fn from(index: Option<$index>) -> Self {
482                if let Some(index) = index {
483                    Self::new_some(index.0)
484                } else {
485                    Self::new_none()
486                }
487            }
488        }
489
490        impl From<$index> for $optional_index {
491            fn from(index: $index) -> Self {
492                Self::new_some(index.0)
493            }
494        }
495
496        impl From<$optional_index> for Option<$index> {
497            fn from(optional_index: $optional_index) -> Self {
498                if optional_index.is_some() {
499                    Some($index::new(optional_index.0))
500                } else {
501                    None
502                }
503            }
504        }
505
506        ////////////////////////////////////
507        ////// Conversions with usize //////
508        ////////////////////////////////////
509
510        impl From<usize> for $index {
511            fn from(value: usize) -> Self {
512                Self::new(
513                    value
514                        .try_into()
515                        .ok()
516                        .expect("index conversion from usize failed"),
517                )
518            }
519        }
520
521        impl From<usize> for $optional_index {
522            fn from(value: usize) -> Self {
523                Self::new_some(
524                    value
525                        .try_into()
526                        .ok()
527                        .expect("index conversion from usize failed"),
528                )
529            }
530        }
531
532        impl From<Option<usize>> for $optional_index {
533            fn from(value: Option<usize>) -> Self {
534                if let Some(value) = value {
535                    Self::new_some(
536                        value
537                            .try_into()
538                            .ok()
539                            .expect("index conversion from usize failed"),
540                    )
541                } else {
542                    Self::new_none()
543                }
544            }
545        }
546
547        impl From<$index> for usize {
548            fn from(index: $index) -> Self {
549                index
550                    .0
551                    .try_into()
552                    .ok()
553                    .expect("index conversion to usize failed")
554            }
555        }
556
557        impl From<$optional_index> for Option<usize> {
558            fn from(index: $optional_index) -> Self {
559                if index.is_some() {
560                    Some(
561                        index
562                            .0
563                            .try_into()
564                            .ok()
565                            .expect("index conversion to usize failed"),
566                    )
567                } else {
568                    None
569                }
570            }
571        }
572
573        ////////////////////////
574        ////// Formatting //////
575        ////////////////////////
576
577        impl std::fmt::Debug for $index {
578            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579                write!(f, "{}({:?})", stringify!($index), self.0)
580            }
581        }
582
583        impl std::fmt::Debug for $optional_index {
584            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585                if self.is_some() {
586                    write!(f, "{}({:?})", stringify!($optional_index), self.0)
587                } else {
588                    write!(f, "{}(None)", stringify!($optional_index))
589                }
590            }
591        }
592
593        impl std::fmt::Display for $index {
594            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
595                write!(f, "{}", self.0)
596            }
597        }
598
599        impl std::fmt::Display for $optional_index {
600            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601                if self.is_some() {
602                    write!(f, "{}", self.0)
603                } else {
604                    write!(f, "None")
605                }
606            }
607        }
608
609        //////////////////////////
610        ////// Clone + Copy //////
611        //////////////////////////
612
613        impl Clone for $index {
614            fn clone(&self) -> Self {
615                *self
616            }
617        }
618
619        impl Clone for $optional_index {
620            fn clone(&self) -> Self {
621                *self
622            }
623        }
624
625        impl Copy for $index {}
626
627        impl Copy for $optional_index {}
628
629        //////////////////////
630        ////// Equality //////
631        //////////////////////
632
633        impl PartialEq for $index {
634            fn eq(&self, other: &Self) -> bool {
635                self.0.eq(&other.0)
636            }
637        }
638
639        impl PartialEq for $optional_index {
640            fn eq(&self, other: &Self) -> bool {
641                self.0.eq(&other.0)
642            }
643        }
644
645        impl Eq for $index {}
646
647        impl Eq for $optional_index {}
648
649        //////////////////////
650        ////// Ordering //////
651        //////////////////////
652
653        impl PartialOrd for $index {
654            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
655                Some(self.cmp(other))
656            }
657        }
658
659        impl PartialOrd for $optional_index {
660            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
661                Some(self.cmp(other))
662            }
663        }
664
665        impl Ord for $index {
666            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
667                self.0.cmp(&other.0)
668            }
669        }
670
671        impl Ord for $optional_index {
672            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
673                self.0.cmp(&other.0)
674            }
675        }
676
677        /////////////////////
678        ////// Hashing //////
679        /////////////////////
680
681        impl std::hash::Hash for $index {
682            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
683                self.0.hash(state);
684            }
685        }
686
687        impl std::hash::Hash for $optional_index {
688            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
689                self.0.hash(state);
690            }
691        }
692    };
693}