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> {
52 #[allow(dead_code)]
53 pub fn new(value: IndexType) -> Self
54 where
55 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
56 {
57 assert_ne!(value, IndexType::max_value());
58 Self(value)
59 }
60
61 #[allow(dead_code)]
62 pub fn from_usize(value: usize) -> Self
63 where
64 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
65 {
66 Self::new(
67 value
68 .try_into()
69 .ok()
70 .expect("index conversion from usize failed"),
71 )
72 }
73
74 #[allow(dead_code)]
75 pub fn into_usize(self) -> usize
76 where
77 IndexType: TryInto<usize>,
78 {
79 self.0
80 .try_into()
81 .ok()
82 .expect("index conversion to usize failed")
83 }
84
85 #[allow(dead_code)]
86 pub fn from_raw(value: IndexType) -> Self
87 where
88 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
89 {
90 Self::new(value)
91 }
92
93 #[allow(dead_code)]
94 pub fn into_raw(self) -> IndexType {
95 self.0
96 }
97 }
98
99 impl<IndexType> $optional_index<IndexType> {
104 #[allow(dead_code)]
105 pub fn new_some(value: IndexType) -> Self
106 where
107 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
108 {
109 assert_ne!(value, IndexType::max_value());
110 Self(value)
111 }
112
113 #[allow(dead_code)]
114 pub fn new_none() -> Self
115 where
116 IndexType: num_traits::bounds::UpperBounded,
117 {
118 Self(IndexType::max_value())
119 }
120
121 #[allow(dead_code)]
122 pub fn from_option(option: Option<$index<IndexType>>) -> Self
123 where
124 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
125 {
126 Self::from(option)
127 }
128
129 #[allow(dead_code)]
130 pub fn into_option(self) -> Option<$index<IndexType>>
131 where
132 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
133 {
134 self.into()
135 }
136
137 #[allow(dead_code)]
138 pub fn unwrap(self) -> $index<IndexType>
139 where
140 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
141 {
142 if self.is_some() {
143 $index::new(self.0)
144 } else {
145 panic!("called `unwrap()` on a `None` value");
146 }
147 }
148
149 #[allow(dead_code)]
150 pub fn from_usize(value: usize) -> Self
151 where
152 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
153 {
154 Self::new_some(
155 value
156 .try_into()
157 .ok()
158 .expect("index conversion from usize failed"),
159 )
160 }
161
162 #[allow(dead_code)]
163 pub fn from_option_usize(value: Option<usize>) -> Self
164 where
165 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
166 {
167 if let Some(value) = value {
168 Self::new_some(
169 value
170 .try_into()
171 .ok()
172 .expect("index conversion from usize failed"),
173 )
174 } else {
175 Self::new_none()
176 }
177 }
178
179 #[allow(dead_code)]
180 pub fn into_usize(self) -> Option<usize>
181 where
182 IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>,
183 {
184 if self.is_some() {
185 Some(
186 self.0
187 .try_into()
188 .ok()
189 .expect("index conversion to usize failed"),
190 )
191 } else {
192 None
193 }
194 }
195
196 #[allow(dead_code)]
197 pub fn from_raw(value: Option<IndexType>) -> Self
198 where
199 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
200 {
201 if let Some(value) = value {
202 Self::new_some(value)
203 } else {
204 Self::new_none()
205 }
206 }
207
208 #[allow(dead_code)]
209 pub fn into_raw(self) -> Option<IndexType>
210 where
211 IndexType: num_traits::bounds::UpperBounded + Eq,
212 {
213 if self.is_some() { Some(self.0) } else { None }
214 }
215
216 #[allow(dead_code)]
217 pub fn is_some(&self) -> bool
218 where
219 IndexType: num_traits::bounds::UpperBounded + Eq,
220 {
221 self.0 != IndexType::max_value()
222 }
223
224 #[allow(dead_code)]
225 pub fn is_none(&self) -> bool
226 where
227 IndexType: num_traits::bounds::UpperBounded + Eq,
228 {
229 self.0 == IndexType::max_value()
230 }
231 }
232
233 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
238 From<Option<$index<IndexType>>> for $optional_index<IndexType>
239 {
240 fn from(index: Option<$index<IndexType>>) -> Self {
241 if let Some(index) = index {
242 Self::new_some(index.0)
243 } else {
244 Self::new_none()
245 }
246 }
247 }
248
249 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
250 From<$index<IndexType>> for $optional_index<IndexType>
251 {
252 fn from(index: $index<IndexType>) -> Self {
253 Self::new_some(index.0)
254 }
255 }
256
257 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
258 From<$optional_index<IndexType>> for Option<$index<IndexType>>
259 {
260 fn from(optional_index: $optional_index<IndexType>) -> Self {
261 if optional_index.is_some() {
262 Some($index::new(optional_index.0))
263 } else {
264 None
265 }
266 }
267 }
268
269 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
274 From<usize> for $index<IndexType>
275 {
276 fn from(value: usize) -> Self {
277 Self::new(
278 value
279 .try_into()
280 .ok()
281 .expect("index conversion from usize failed"),
282 )
283 }
284 }
285
286 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
287 From<usize> for $optional_index<IndexType>
288 {
289 fn from(value: usize) -> Self {
290 Self::new_some(
291 value
292 .try_into()
293 .ok()
294 .expect("index conversion from usize failed"),
295 )
296 }
297 }
298
299 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
300 From<Option<usize>> for $optional_index<IndexType>
301 {
302 fn from(value: Option<usize>) -> Self {
303 if let Some(value) = value {
304 Self::new_some(
305 value
306 .try_into()
307 .ok()
308 .expect("index conversion from usize failed"),
309 )
310 } else {
311 Self::new_none()
312 }
313 }
314 }
315
316 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryInto<usize>>
317 From<$index<IndexType>> for usize
318 {
319 fn from(value: $index<IndexType>) -> Self {
320 value
321 .0
322 .try_into()
323 .ok()
324 .expect("index conversion from usize failed")
325 }
326 }
327
328 impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
329 From<$optional_index<IndexType>> for Option<usize>
330 {
331 fn from(index: $optional_index<IndexType>) -> Self {
332 if index.is_some() {
333 Some(
334 index
335 .0
336 .try_into()
337 .ok()
338 .expect("index conversion to usize failed"),
339 )
340 } else {
341 None
342 }
343 }
344 }
345
346 impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
351 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352 write!(f, "{}({:?})", stringify!($index), self.0)
353 }
354 }
355
356 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
357 for $optional_index<IndexType>
358 {
359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360 if self.is_some() {
361 write!(f, "{}({:?})", stringify!($optional_index), self.0)
362 } else {
363 write!(f, "{}(None)", stringify!($optional_index))
364 }
365 }
366 }
367
368 impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
370 write!(f, "{}", self.0)
371 }
372 }
373
374 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
375 for $optional_index<IndexType>
376 {
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 if self.is_some() {
379 write!(f, "{}", self.0)
380 } else {
381 write!(f, "None")
382 }
383 }
384 }
385
386 impl<IndexType: Clone> Clone for $index<IndexType> {
391 fn clone(&self) -> Self {
392 Self(self.0.clone())
393 }
394 }
395
396 impl<IndexType: Clone> Clone for $optional_index<IndexType> {
397 fn clone(&self) -> Self {
398 Self(self.0.clone())
399 }
400 }
401
402 impl<IndexType: Copy> Copy for $index<IndexType> {}
403
404 impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
405
406 impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
411 fn eq(&self, other: &Self) -> bool {
412 self.0.eq(&other.0)
413 }
414 }
415
416 impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
417 fn eq(&self, other: &Self) -> bool {
418 self.0.eq(&other.0)
419 }
420 }
421
422 impl<IndexType: Eq> Eq for $index<IndexType> {}
423
424 impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
425
426 impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
431 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
432 self.0.partial_cmp(&other.0)
433 }
434 }
435
436 impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
437 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
438 self.0.partial_cmp(&other.0)
439 }
440 }
441
442 impl<IndexType: Ord> Ord for $index<IndexType> {
443 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
444 self.0.cmp(&other.0)
445 }
446 }
447
448 impl<IndexType: Ord> Ord for $optional_index<IndexType> {
449 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
450 self.0.cmp(&other.0)
451 }
452 }
453
454 impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
459 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
460 self.0.hash(state);
461 }
462 }
463
464 impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
465 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
466 self.0.hash(state);
467 }
468 }
469 };
470}
471
472#[macro_export]
473macro_rules! implement_fixed_index {
474 ($index:ident, $optional_index:ident, $index_type:ty) => {
475 struct $index($index_type);
476
477 struct $optional_index($index_type);
478
479 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
480 };
481
482 (pub $index:ident, pub $optional_index:ident, $index_type:ty) => {
483 pub struct $index($index_type);
484
485 pub struct $optional_index($index_type);
486
487 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
488 };
489
490 (pub(crate) $index:ident, pub(crate) $optional_index:ident, $index_type:ty) => {
491 pub(crate) struct $index($index_type);
492
493 pub(crate) struct $optional_index($index_type);
494
495 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
496 };
497
498 (pub(super) $index:ident, pub(super) $optional_index:ident, $index_type:ty) => {
499 pub(super) struct $index($index_type);
500
501 pub(super) struct $optional_index($index_type);
502
503 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
504 };
505
506 (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident, $index_type:ty) => {
507 pub(in $index_visibility) struct $index($index_type);
508
509 pub(in $optional_index_visibility) struct $optional_index($index_type);
510
511 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
512 };
513
514 ($index:ident, $optional_index:ident, $index_type:ty, __inner__) => {
515 impl $index {
520 #[allow(dead_code)]
521 pub fn new(value: $index_type) -> Self {
522 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
523 Self(value)
524 }
525
526 #[allow(dead_code)]
527 pub fn from_usize(value: usize) -> Self {
528 Self::new(
529 value
530 .try_into()
531 .ok()
532 .expect("index conversion from usize failed"),
533 )
534 }
535
536 #[allow(dead_code)]
537 pub fn into_usize(self) -> usize {
538 self.0
539 .try_into()
540 .ok()
541 .expect("index conversion to usize failed")
542 }
543
544 #[allow(dead_code)]
545 pub fn from_raw(value: $index_type) -> Self {
546 Self::new(value)
547 }
548
549 #[allow(dead_code)]
550 pub fn into_raw(self) -> $index_type {
551 self.0
552 }
553 }
554
555 impl $optional_index {
560 #[allow(dead_code)]
561 pub fn new_some(value: $index_type) -> Self {
562 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
563 Self(value)
564 }
565
566 #[allow(dead_code)]
567 pub fn new_none() -> Self {
568 Self(num_traits::bounds::UpperBounded::max_value())
569 }
570
571 #[allow(dead_code)]
572 pub fn from_option(option: Option<$index>) -> Self {
573 Self::from(option)
574 }
575
576 #[allow(dead_code)]
577 pub fn into_option(self) -> Option<$index> {
578 self.into()
579 }
580
581 #[allow(dead_code)]
582 pub fn unwrap(self) -> $index {
583 if self.is_some() {
584 $index::new(self.0)
585 } else {
586 panic!("called `unwrap()` on a `None` value");
587 }
588 }
589
590 #[allow(dead_code)]
591 pub fn from_usize(value: usize) -> Self {
592 Self::new_some(
593 value
594 .try_into()
595 .ok()
596 .expect("index conversion from usize failed"),
597 )
598 }
599
600 #[allow(dead_code)]
601 pub fn from_option_usize(value: Option<usize>) -> Self {
602 if let Some(value) = value {
603 Self::new_some(
604 value
605 .try_into()
606 .ok()
607 .expect("index conversion from usize failed"),
608 )
609 } else {
610 Self::new_none()
611 }
612 }
613
614 #[allow(dead_code)]
615 pub fn into_usize(self) -> Option<usize> {
616 if self.is_some() {
617 Some(
618 self.0
619 .try_into()
620 .ok()
621 .expect("index conversion to usize failed"),
622 )
623 } else {
624 None
625 }
626 }
627
628 #[allow(dead_code)]
629 pub fn from_raw(value: Option<$index_type>) -> Self {
630 if let Some(value) = value {
631 Self::new_some(value)
632 } else {
633 Self::new_none()
634 }
635 }
636
637 #[allow(dead_code)]
638 pub fn into_raw(self) -> Option<$index_type> {
639 if self.is_some() { Some(self.0) } else { None }
640 }
641
642 #[allow(dead_code)]
643 pub fn is_some(&self) -> bool {
644 self.0 != num_traits::bounds::UpperBounded::max_value()
645 }
646
647 #[allow(dead_code)]
648 pub fn is_none(&self) -> bool {
649 self.0 == num_traits::bounds::UpperBounded::max_value()
650 }
651 }
652
653 impl From<Option<$index>> for $optional_index {
658 fn from(index: Option<$index>) -> Self {
659 if let Some(index) = index {
660 Self::new_some(index.0)
661 } else {
662 Self::new_none()
663 }
664 }
665 }
666
667 impl From<$index> for $optional_index {
668 fn from(index: $index) -> Self {
669 Self::new_some(index.0)
670 }
671 }
672
673 impl From<$optional_index> for Option<$index> {
674 fn from(optional_index: $optional_index) -> Self {
675 if optional_index.is_some() {
676 Some($index::new(optional_index.0))
677 } else {
678 None
679 }
680 }
681 }
682
683 impl From<usize> for $index {
688 fn from(value: usize) -> Self {
689 Self::new(
690 value
691 .try_into()
692 .ok()
693 .expect("index conversion from usize failed"),
694 )
695 }
696 }
697
698 impl From<usize> for $optional_index {
699 fn from(value: usize) -> Self {
700 Self::new_some(
701 value
702 .try_into()
703 .ok()
704 .expect("index conversion from usize failed"),
705 )
706 }
707 }
708
709 impl From<Option<usize>> for $optional_index {
710 fn from(value: Option<usize>) -> Self {
711 if let Some(value) = value {
712 Self::new_some(
713 value
714 .try_into()
715 .ok()
716 .expect("index conversion from usize failed"),
717 )
718 } else {
719 Self::new_none()
720 }
721 }
722 }
723
724 impl From<$index> for usize {
725 fn from(index: $index) -> Self {
726 index
727 .0
728 .try_into()
729 .ok()
730 .expect("index conversion to usize failed")
731 }
732 }
733
734 impl From<$optional_index> for Option<usize> {
735 fn from(index: $optional_index) -> Self {
736 if index.is_some() {
737 Some(
738 index
739 .0
740 .try_into()
741 .ok()
742 .expect("index conversion to usize failed"),
743 )
744 } else {
745 None
746 }
747 }
748 }
749
750 impl std::fmt::Debug for $index {
755 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
756 write!(f, "{}({:?})", stringify!($index), self.0)
757 }
758 }
759
760 impl std::fmt::Debug for $optional_index {
761 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762 if self.is_some() {
763 write!(f, "{}({:?})", stringify!($optional_index), self.0)
764 } else {
765 write!(f, "{}(None)", stringify!($optional_index))
766 }
767 }
768 }
769
770 impl std::fmt::Display for $index {
771 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772 write!(f, "{}", self.0)
773 }
774 }
775
776 impl std::fmt::Display for $optional_index {
777 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
778 if self.is_some() {
779 write!(f, "{}", self.0)
780 } else {
781 write!(f, "None")
782 }
783 }
784 }
785
786 impl Clone for $index {
791 fn clone(&self) -> Self {
792 *self
793 }
794 }
795
796 impl Clone for $optional_index {
797 fn clone(&self) -> Self {
798 *self
799 }
800 }
801
802 impl Copy for $index {}
803
804 impl Copy for $optional_index {}
805
806 impl PartialEq for $index {
811 fn eq(&self, other: &Self) -> bool {
812 self.0.eq(&other.0)
813 }
814 }
815
816 impl PartialEq for $optional_index {
817 fn eq(&self, other: &Self) -> bool {
818 self.0.eq(&other.0)
819 }
820 }
821
822 impl Eq for $index {}
823
824 impl Eq for $optional_index {}
825
826 impl PartialOrd for $index {
831 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
832 Some(self.cmp(other))
833 }
834 }
835
836 impl PartialOrd for $optional_index {
837 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
838 Some(self.cmp(other))
839 }
840 }
841
842 impl Ord for $index {
843 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
844 self.0.cmp(&other.0)
845 }
846 }
847
848 impl Ord for $optional_index {
849 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
850 self.0.cmp(&other.0)
851 }
852 }
853
854 impl std::hash::Hash for $index {
859 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
860 self.0.hash(state);
861 }
862 }
863
864 impl std::hash::Hash for $optional_index {
865 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
866 self.0.hash(state);
867 }
868 }
869 };
870}