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