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 expect(self, message: &str) -> $index<IndexType>
139 where
140 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
141 {
142 self.into_option().expect(message)
143 }
144
145 #[allow(dead_code)]
146 pub fn unwrap(self) -> $index<IndexType>
147 where
148 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
149 {
150 self.into_option().unwrap()
151 }
152
153 #[allow(dead_code)]
154 pub fn unwrap_or(self, default: $index<IndexType>) -> $index<IndexType>
155 where
156 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
157 {
158 self.into_option().unwrap_or(default)
159 }
160
161 #[allow(dead_code)]
162 pub fn unwrap_or_else(
163 self,
164 default: impl FnOnce() -> $index<IndexType>,
165 ) -> $index<IndexType>
166 where
167 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
168 {
169 self.into_option().unwrap_or_else(default)
170 }
171
172 #[allow(dead_code)]
173 pub unsafe fn unwrap_unchecked(self) -> $index<IndexType>
174 where
175 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
176 {
177 unsafe { self.into_option().unwrap_unchecked() }
178 }
179
180 #[allow(dead_code)]
181 pub fn from_usize(value: usize) -> Self
182 where
183 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
184 {
185 Self::new_some(
186 value
187 .try_into()
188 .ok()
189 .expect("index conversion from usize failed"),
190 )
191 }
192
193 #[allow(dead_code)]
194 pub fn from_option_usize(value: Option<usize>) -> Self
195 where
196 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
197 {
198 if let Some(value) = value {
199 Self::new_some(
200 value
201 .try_into()
202 .ok()
203 .expect("index conversion from usize failed"),
204 )
205 } else {
206 Self::new_none()
207 }
208 }
209
210 #[allow(dead_code)]
211 pub fn into_usize(self) -> Option<usize>
212 where
213 IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>,
214 {
215 if self.is_some() {
216 Some(
217 self.0
218 .try_into()
219 .ok()
220 .expect("index conversion to usize failed"),
221 )
222 } else {
223 None
224 }
225 }
226
227 #[allow(dead_code)]
228 pub fn from_raw(value: Option<IndexType>) -> Self
229 where
230 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
231 {
232 if let Some(value) = value {
233 Self::new_some(value)
234 } else {
235 Self::new_none()
236 }
237 }
238
239 #[allow(dead_code)]
240 pub fn into_raw(self) -> Option<IndexType>
241 where
242 IndexType: num_traits::bounds::UpperBounded + Eq,
243 {
244 if self.is_some() { Some(self.0) } else { None }
245 }
246
247 #[allow(dead_code)]
248 pub fn is_some(&self) -> bool
249 where
250 IndexType: num_traits::bounds::UpperBounded + Eq,
251 {
252 self.0 != IndexType::max_value()
253 }
254
255 #[allow(dead_code)]
256 pub fn is_none(&self) -> bool
257 where
258 IndexType: num_traits::bounds::UpperBounded + Eq,
259 {
260 self.0 == IndexType::max_value()
261 }
262 }
263
264 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
269 From<Option<$index<IndexType>>> for $optional_index<IndexType>
270 {
271 fn from(index: Option<$index<IndexType>>) -> Self {
272 if let Some(index) = index {
273 Self::new_some(index.0)
274 } else {
275 Self::new_none()
276 }
277 }
278 }
279
280 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
281 From<$index<IndexType>> for $optional_index<IndexType>
282 {
283 fn from(index: $index<IndexType>) -> Self {
284 Self::new_some(index.0)
285 }
286 }
287
288 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
289 From<$optional_index<IndexType>> for Option<$index<IndexType>>
290 {
291 fn from(optional_index: $optional_index<IndexType>) -> Self {
292 if optional_index.is_some() {
293 Some($index::new(optional_index.0))
294 } else {
295 None
296 }
297 }
298 }
299
300 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
305 From<usize> for $index<IndexType>
306 {
307 fn from(value: usize) -> Self {
308 Self::new(
309 value
310 .try_into()
311 .ok()
312 .expect("index conversion from usize failed"),
313 )
314 }
315 }
316
317 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
318 From<usize> for $optional_index<IndexType>
319 {
320 fn from(value: usize) -> Self {
321 Self::new_some(
322 value
323 .try_into()
324 .ok()
325 .expect("index conversion from usize failed"),
326 )
327 }
328 }
329
330 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
331 From<Option<usize>> for $optional_index<IndexType>
332 {
333 fn from(value: Option<usize>) -> Self {
334 if let Some(value) = value {
335 Self::new_some(
336 value
337 .try_into()
338 .ok()
339 .expect("index conversion from usize failed"),
340 )
341 } else {
342 Self::new_none()
343 }
344 }
345 }
346
347 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryInto<usize>>
348 From<$index<IndexType>> for usize
349 {
350 fn from(value: $index<IndexType>) -> Self {
351 value
352 .0
353 .try_into()
354 .ok()
355 .expect("index conversion from usize failed")
356 }
357 }
358
359 impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
360 From<$optional_index<IndexType>> for Option<usize>
361 {
362 fn from(index: $optional_index<IndexType>) -> Self {
363 if index.is_some() {
364 Some(
365 index
366 .0
367 .try_into()
368 .ok()
369 .expect("index conversion to usize failed"),
370 )
371 } else {
372 None
373 }
374 }
375 }
376
377 impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383 write!(f, "{}({:?})", stringify!($index), self.0)
384 }
385 }
386
387 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
388 for $optional_index<IndexType>
389 {
390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 if self.is_some() {
392 write!(f, "{}({:?})", stringify!($optional_index), self.0)
393 } else {
394 write!(f, "{}(None)", stringify!($optional_index))
395 }
396 }
397 }
398
399 impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
400 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
401 write!(f, "{}", self.0)
402 }
403 }
404
405 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
406 for $optional_index<IndexType>
407 {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 if self.is_some() {
410 write!(f, "{}", self.0)
411 } else {
412 write!(f, "None")
413 }
414 }
415 }
416
417 impl<IndexType: Clone> Clone for $index<IndexType> {
422 fn clone(&self) -> Self {
423 Self(self.0.clone())
424 }
425 }
426
427 impl<IndexType: Clone> Clone for $optional_index<IndexType> {
428 fn clone(&self) -> Self {
429 Self(self.0.clone())
430 }
431 }
432
433 impl<IndexType: Copy> Copy for $index<IndexType> {}
434
435 impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
436
437 impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
442 fn eq(&self, other: &Self) -> bool {
443 self.0.eq(&other.0)
444 }
445 }
446
447 impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
448 fn eq(&self, other: &Self) -> bool {
449 self.0.eq(&other.0)
450 }
451 }
452
453 impl<IndexType: Eq> Eq for $index<IndexType> {}
454
455 impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
456
457 impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
462 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
463 self.0.partial_cmp(&other.0)
464 }
465 }
466
467 impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
468 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
469 self.0.partial_cmp(&other.0)
470 }
471 }
472
473 impl<IndexType: Ord> Ord for $index<IndexType> {
474 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
475 self.0.cmp(&other.0)
476 }
477 }
478
479 impl<IndexType: Ord> Ord for $optional_index<IndexType> {
480 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
481 self.0.cmp(&other.0)
482 }
483 }
484
485 impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
490 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
491 self.0.hash(state);
492 }
493 }
494
495 impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
496 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
497 self.0.hash(state);
498 }
499 }
500 };
501}
502
503#[macro_export]
504macro_rules! implement_fixed_index {
505 ($index:ident, $optional_index:ident, $index_type:ty) => {
506 struct $index($index_type);
507
508 struct $optional_index($index_type);
509
510 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
511 };
512
513 (pub $index:ident, pub $optional_index:ident, $index_type:ty) => {
514 pub struct $index($index_type);
515
516 pub struct $optional_index($index_type);
517
518 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
519 };
520
521 (pub(crate) $index:ident, pub(crate) $optional_index:ident, $index_type:ty) => {
522 pub(crate) struct $index($index_type);
523
524 pub(crate) struct $optional_index($index_type);
525
526 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
527 };
528
529 (pub(super) $index:ident, pub(super) $optional_index:ident, $index_type:ty) => {
530 pub(super) struct $index($index_type);
531
532 pub(super) struct $optional_index($index_type);
533
534 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
535 };
536
537 (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident, $index_type:ty) => {
538 pub(in $index_visibility) struct $index($index_type);
539
540 pub(in $optional_index_visibility) struct $optional_index($index_type);
541
542 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
543 };
544
545 ($index:ident, $optional_index:ident, $index_type:ty, __inner__) => {
546 impl $index {
551 #[allow(dead_code)]
552 pub fn new(value: $index_type) -> Self {
553 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
554 Self(value)
555 }
556
557 #[allow(dead_code)]
558 pub fn from_usize(value: usize) -> Self {
559 Self::new(
560 value
561 .try_into()
562 .ok()
563 .expect("index conversion from usize failed"),
564 )
565 }
566
567 #[allow(dead_code)]
568 pub fn into_usize(self) -> usize {
569 self.0
570 .try_into()
571 .ok()
572 .expect("index conversion to usize failed")
573 }
574
575 #[allow(dead_code)]
576 pub fn from_raw(value: $index_type) -> Self {
577 Self::new(value)
578 }
579
580 #[allow(dead_code)]
581 pub fn into_raw(self) -> $index_type {
582 self.0
583 }
584 }
585
586 impl $optional_index {
591 #[allow(dead_code)]
592 pub fn new_some(value: $index_type) -> Self {
593 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
594 Self(value)
595 }
596
597 #[allow(dead_code)]
598 pub fn new_none() -> Self {
599 Self(num_traits::bounds::UpperBounded::max_value())
600 }
601
602 #[allow(dead_code)]
603 pub fn from_option(option: Option<$index>) -> Self {
604 Self::from(option)
605 }
606
607 #[allow(dead_code)]
608 pub fn into_option(self) -> Option<$index> {
609 self.into()
610 }
611
612 #[allow(dead_code)]
613 pub fn expect(self, message: &str) -> $index {
614 self.into_option().expect(message)
615 }
616
617 #[allow(dead_code)]
618 pub fn unwrap(self) -> $index {
619 self.into_option().unwrap()
620 }
621
622 #[allow(dead_code)]
623 pub fn unwrap_or(self, default: $index) -> $index {
624 self.into_option().unwrap_or(default)
625 }
626
627 #[allow(dead_code)]
628 pub fn unwrap_or_else(self, default: impl FnOnce() -> $index) -> $index {
629 self.into_option().unwrap_or_else(default)
630 }
631
632 #[allow(dead_code)]
633 pub unsafe fn unwrap_unchecked(self) -> $index {
634 unsafe { self.into_option().unwrap_unchecked() }
635 }
636
637 #[allow(dead_code)]
638 pub fn from_usize(value: usize) -> Self {
639 Self::new_some(
640 value
641 .try_into()
642 .ok()
643 .expect("index conversion from usize failed"),
644 )
645 }
646
647 #[allow(dead_code)]
648 pub fn from_option_usize(value: Option<usize>) -> Self {
649 if let Some(value) = value {
650 Self::new_some(
651 value
652 .try_into()
653 .ok()
654 .expect("index conversion from usize failed"),
655 )
656 } else {
657 Self::new_none()
658 }
659 }
660
661 #[allow(dead_code)]
662 pub fn into_usize(self) -> Option<usize> {
663 if self.is_some() {
664 Some(
665 self.0
666 .try_into()
667 .ok()
668 .expect("index conversion to usize failed"),
669 )
670 } else {
671 None
672 }
673 }
674
675 #[allow(dead_code)]
676 pub fn from_raw(value: Option<$index_type>) -> Self {
677 if let Some(value) = value {
678 Self::new_some(value)
679 } else {
680 Self::new_none()
681 }
682 }
683
684 #[allow(dead_code)]
685 pub fn into_raw(self) -> Option<$index_type> {
686 if self.is_some() { Some(self.0) } else { None }
687 }
688
689 #[allow(dead_code)]
690 pub fn is_some(&self) -> bool {
691 self.0 != num_traits::bounds::UpperBounded::max_value()
692 }
693
694 #[allow(dead_code)]
695 pub fn is_none(&self) -> bool {
696 self.0 == num_traits::bounds::UpperBounded::max_value()
697 }
698 }
699
700 impl From<Option<$index>> for $optional_index {
705 fn from(index: Option<$index>) -> Self {
706 if let Some(index) = index {
707 Self::new_some(index.0)
708 } else {
709 Self::new_none()
710 }
711 }
712 }
713
714 impl From<$index> for $optional_index {
715 fn from(index: $index) -> Self {
716 Self::new_some(index.0)
717 }
718 }
719
720 impl From<$optional_index> for Option<$index> {
721 fn from(optional_index: $optional_index) -> Self {
722 if optional_index.is_some() {
723 Some($index::new(optional_index.0))
724 } else {
725 None
726 }
727 }
728 }
729
730 impl From<usize> for $index {
735 fn from(value: usize) -> Self {
736 Self::new(
737 value
738 .try_into()
739 .ok()
740 .expect("index conversion from usize failed"),
741 )
742 }
743 }
744
745 impl From<usize> for $optional_index {
746 fn from(value: usize) -> Self {
747 Self::new_some(
748 value
749 .try_into()
750 .ok()
751 .expect("index conversion from usize failed"),
752 )
753 }
754 }
755
756 impl From<Option<usize>> for $optional_index {
757 fn from(value: Option<usize>) -> Self {
758 if let Some(value) = value {
759 Self::new_some(
760 value
761 .try_into()
762 .ok()
763 .expect("index conversion from usize failed"),
764 )
765 } else {
766 Self::new_none()
767 }
768 }
769 }
770
771 impl From<$index> for usize {
772 fn from(index: $index) -> Self {
773 index
774 .0
775 .try_into()
776 .ok()
777 .expect("index conversion to usize failed")
778 }
779 }
780
781 impl From<$optional_index> for Option<usize> {
782 fn from(index: $optional_index) -> Self {
783 if index.is_some() {
784 Some(
785 index
786 .0
787 .try_into()
788 .ok()
789 .expect("index conversion to usize failed"),
790 )
791 } else {
792 None
793 }
794 }
795 }
796
797 impl std::fmt::Debug for $index {
802 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
803 write!(f, "{}({:?})", stringify!($index), self.0)
804 }
805 }
806
807 impl std::fmt::Debug for $optional_index {
808 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
809 if self.is_some() {
810 write!(f, "{}({:?})", stringify!($optional_index), self.0)
811 } else {
812 write!(f, "{}(None)", stringify!($optional_index))
813 }
814 }
815 }
816
817 impl std::fmt::Display for $index {
818 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
819 write!(f, "{}", self.0)
820 }
821 }
822
823 impl std::fmt::Display for $optional_index {
824 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
825 if self.is_some() {
826 write!(f, "{}", self.0)
827 } else {
828 write!(f, "None")
829 }
830 }
831 }
832
833 impl Clone for $index {
838 fn clone(&self) -> Self {
839 *self
840 }
841 }
842
843 impl Clone for $optional_index {
844 fn clone(&self) -> Self {
845 *self
846 }
847 }
848
849 impl Copy for $index {}
850
851 impl Copy for $optional_index {}
852
853 impl PartialEq for $index {
858 fn eq(&self, other: &Self) -> bool {
859 self.0.eq(&other.0)
860 }
861 }
862
863 impl PartialEq for $optional_index {
864 fn eq(&self, other: &Self) -> bool {
865 self.0.eq(&other.0)
866 }
867 }
868
869 impl Eq for $index {}
870
871 impl Eq for $optional_index {}
872
873 impl PartialOrd for $index {
878 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
879 Some(self.cmp(other))
880 }
881 }
882
883 impl PartialOrd for $optional_index {
884 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
885 Some(self.cmp(other))
886 }
887 }
888
889 impl Ord for $index {
890 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
891 self.0.cmp(&other.0)
892 }
893 }
894
895 impl Ord for $optional_index {
896 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
897 self.0.cmp(&other.0)
898 }
899 }
900
901 impl std::hash::Hash for $index {
906 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
907 self.0.hash(state);
908 }
909 }
910
911 impl std::hash::Hash for $optional_index {
912 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
913 self.0.hash(state);
914 }
915 }
916 };
917}