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 into_usize(self) -> usize
72 where
73 IndexType: TryInto<usize>,
74 {
75 self.0
76 .try_into()
77 .ok()
78 .expect("index conversion to usize failed")
79 }
80
81 #[allow(dead_code)]
82 pub fn from_raw(value: IndexType) -> Self
83 where
84 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
85 {
86 Self::new(value)
87 }
88
89 #[allow(dead_code)]
90 pub fn into_raw(self) -> IndexType {
91 self.0
92 }
93 }
94
95 impl<IndexType> $optional_index<IndexType> {
96 #[allow(dead_code)]
97 pub fn new_some(value: IndexType) -> Self
98 where
99 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
100 {
101 assert_ne!(value, IndexType::max_value());
102 Self(value)
103 }
104
105 #[allow(dead_code)]
106 pub fn new_none() -> Self
107 where
108 IndexType: num_traits::bounds::UpperBounded,
109 {
110 Self(IndexType::max_value())
111 }
112
113 #[allow(dead_code)]
114 pub fn from_usize(value: usize) -> Self
115 where
116 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
117 {
118 Self::new_some(
119 value
120 .try_into()
121 .ok()
122 .expect("index conversion from usize failed"),
123 )
124 }
125
126 #[allow(dead_code)]
127 pub fn from_option_usize(value: Option<usize>) -> Self
128 where
129 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
130 {
131 if let Some(value) = value {
132 Self::new_some(
133 value
134 .try_into()
135 .ok()
136 .expect("index conversion from usize failed"),
137 )
138 } else {
139 Self::new_none()
140 }
141 }
142
143 #[allow(dead_code)]
144 pub fn into_usize(self) -> Option<usize>
145 where
146 IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>,
147 {
148 if self.is_some() {
149 Some(
150 self.0
151 .try_into()
152 .ok()
153 .expect("index conversion to usize failed"),
154 )
155 } else {
156 None
157 }
158 }
159
160 #[allow(dead_code)]
161 pub fn from_raw(value: Option<IndexType>) -> Self
162 where
163 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
164 {
165 if let Some(value) = value {
166 Self::new_some(value)
167 } else {
168 Self::new_none()
169 }
170 }
171
172 #[allow(dead_code)]
173 pub fn into_raw(self) -> Option<IndexType>
174 where
175 IndexType: num_traits::bounds::UpperBounded + Eq,
176 {
177 if self.is_some() { Some(self.0) } else { None }
178 }
179
180 #[allow(dead_code)]
181 pub fn is_some(&self) -> bool
182 where
183 IndexType: num_traits::bounds::UpperBounded + Eq,
184 {
185 self.0 != IndexType::max_value()
186 }
187
188 #[allow(dead_code)]
189 pub fn is_none(&self) -> bool
190 where
191 IndexType: num_traits::bounds::UpperBounded + Eq,
192 {
193 self.0 == IndexType::max_value()
194 }
195 }
196
197 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
202 From<Option<$index<IndexType>>> for $optional_index<IndexType>
203 {
204 fn from(index: Option<$index<IndexType>>) -> Self {
205 if let Some(index) = index {
206 Self::new_some(index.0)
207 } else {
208 Self::new_none()
209 }
210 }
211 }
212
213 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
214 From<$index<IndexType>> for $optional_index<IndexType>
215 {
216 fn from(index: $index<IndexType>) -> Self {
217 Self::new_some(index.0)
218 }
219 }
220
221 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
222 From<$optional_index<IndexType>> for Option<$index<IndexType>>
223 {
224 fn from(optional_index: $optional_index<IndexType>) -> Self {
225 if optional_index.is_some() {
226 Some($index::new(optional_index.0))
227 } else {
228 None
229 }
230 }
231 }
232
233 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
238 From<usize> for $index<IndexType>
239 {
240 fn from(value: usize) -> Self {
241 Self::new(
242 value
243 .try_into()
244 .ok()
245 .expect("index conversion from usize failed"),
246 )
247 }
248 }
249
250 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
251 From<usize> for $optional_index<IndexType>
252 {
253 fn from(value: usize) -> Self {
254 Self::new_some(
255 value
256 .try_into()
257 .ok()
258 .expect("index conversion from usize failed"),
259 )
260 }
261 }
262
263 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>>
264 From<Option<usize>> for $optional_index<IndexType>
265 {
266 fn from(value: Option<usize>) -> Self {
267 if let Some(value) = value {
268 Self::new_some(
269 value
270 .try_into()
271 .ok()
272 .expect("index conversion from usize failed"),
273 )
274 } else {
275 Self::new_none()
276 }
277 }
278 }
279
280 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryInto<usize>>
281 From<$index<IndexType>> for usize
282 {
283 fn from(value: $index<IndexType>) -> Self {
284 value
285 .0
286 .try_into()
287 .ok()
288 .expect("index conversion from usize failed")
289 }
290 }
291
292 impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
293 From<$optional_index<IndexType>> for Option<usize>
294 {
295 fn from(index: $optional_index<IndexType>) -> Self {
296 if index.is_some() {
297 Some(
298 index
299 .0
300 .try_into()
301 .ok()
302 .expect("index conversion to usize failed"),
303 )
304 } else {
305 None
306 }
307 }
308 }
309
310 impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316 write!(f, "{}({:?})", stringify!($index), self.0)
317 }
318 }
319
320 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
321 for $optional_index<IndexType>
322 {
323 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 if self.is_some() {
325 write!(f, "{}({:?})", stringify!($optional_index), self.0)
326 } else {
327 write!(f, "{}(None)", stringify!($optional_index))
328 }
329 }
330 }
331
332 impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
334 write!(f, "{}", self.0)
335 }
336 }
337
338 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
339 for $optional_index<IndexType>
340 {
341 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342 if self.is_some() {
343 write!(f, "{}", self.0)
344 } else {
345 write!(f, "None")
346 }
347 }
348 }
349
350 impl<IndexType: Clone> Clone for $index<IndexType> {
355 fn clone(&self) -> Self {
356 Self(self.0.clone())
357 }
358 }
359
360 impl<IndexType: Clone> Clone for $optional_index<IndexType> {
361 fn clone(&self) -> Self {
362 Self(self.0.clone())
363 }
364 }
365
366 impl<IndexType: Copy> Copy for $index<IndexType> {}
367
368 impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
369
370 impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
375 fn eq(&self, other: &Self) -> bool {
376 self.0.eq(&other.0)
377 }
378 }
379
380 impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
381 fn eq(&self, other: &Self) -> bool {
382 self.0.eq(&other.0)
383 }
384 }
385
386 impl<IndexType: Eq> Eq for $index<IndexType> {}
387
388 impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
389
390 impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
395 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
396 self.0.partial_cmp(&other.0)
397 }
398 }
399
400 impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
401 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
402 self.0.partial_cmp(&other.0)
403 }
404 }
405
406 impl<IndexType: Ord> Ord for $index<IndexType> {
407 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
408 self.0.cmp(&other.0)
409 }
410 }
411
412 impl<IndexType: Ord> Ord for $optional_index<IndexType> {
413 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
414 self.0.cmp(&other.0)
415 }
416 }
417
418 impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
423 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
424 self.0.hash(state);
425 }
426 }
427
428 impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
429 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
430 self.0.hash(state);
431 }
432 }
433 };
434}
435
436#[macro_export]
437macro_rules! implement_fixed_index {
438 ($index:ident, $optional_index:ident, $index_type:ty) => {
439 struct $index($index_type);
440
441 struct $optional_index($index_type);
442
443 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
444 };
445
446 (pub $index:ident, pub $optional_index:ident, $index_type:ty) => {
447 pub struct $index($index_type);
448
449 pub struct $optional_index($index_type);
450
451 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
452 };
453
454 (pub(crate) $index:ident, pub(crate) $optional_index:ident, $index_type:ty) => {
455 pub(crate) struct $index($index_type);
456
457 pub(crate) struct $optional_index($index_type);
458
459 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
460 };
461
462 (pub(super) $index:ident, pub(super) $optional_index:ident, $index_type:ty) => {
463 pub(super) struct $index($index_type);
464
465 pub(super) struct $optional_index($index_type);
466
467 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
468 };
469
470 (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident, $index_type:ty) => {
471 pub(in $index_visibility) struct $index($index_type);
472
473 pub(in $optional_index_visibility) struct $optional_index($index_type);
474
475 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
476 };
477
478 ($index:ident, $optional_index:ident, $index_type:ty, __inner__) => {
479 impl $index {
480 #[allow(dead_code)]
481 pub fn new(value: $index_type) -> Self {
482 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
483 Self(value)
484 }
485
486 #[allow(dead_code)]
487 pub fn from_usize(value: usize) -> Self {
488 Self::new(
489 value
490 .try_into()
491 .ok()
492 .expect("index conversion from usize failed"),
493 )
494 }
495
496 #[allow(dead_code)]
497 pub fn into_usize(self) -> usize {
498 self.0
499 .try_into()
500 .ok()
501 .expect("index conversion to usize failed")
502 }
503
504 #[allow(dead_code)]
505 pub fn from_raw(value: $index_type) -> Self {
506 Self::new(value)
507 }
508
509 #[allow(dead_code)]
510 pub fn into_raw(self) -> $index_type {
511 self.0
512 }
513 }
514
515 impl $optional_index {
516 #[allow(dead_code)]
517 pub fn new_some(value: $index_type) -> Self {
518 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
519 Self(value)
520 }
521
522 #[allow(dead_code)]
523 pub fn new_none() -> Self {
524 Self(num_traits::bounds::UpperBounded::max_value())
525 }
526
527 #[allow(dead_code)]
528 pub fn from_usize(value: usize) -> Self {
529 Self::new_some(
530 value
531 .try_into()
532 .ok()
533 .expect("index conversion from usize failed"),
534 )
535 }
536
537 #[allow(dead_code)]
538 pub fn from_option_usize(value: Option<usize>) -> Self {
539 if let Some(value) = value {
540 Self::new_some(
541 value
542 .try_into()
543 .ok()
544 .expect("index conversion from usize failed"),
545 )
546 } else {
547 Self::new_none()
548 }
549 }
550
551 #[allow(dead_code)]
552 pub fn into_usize(self) -> Option<usize> {
553 if self.is_some() {
554 Some(
555 self.0
556 .try_into()
557 .ok()
558 .expect("index conversion to usize failed"),
559 )
560 } else {
561 None
562 }
563 }
564
565 #[allow(dead_code)]
566 pub fn from_raw(value: Option<$index_type>) -> Self {
567 if let Some(value) = value {
568 Self::new_some(value)
569 } else {
570 Self::new_none()
571 }
572 }
573
574 #[allow(dead_code)]
575 pub fn into_raw(self) -> Option<$index_type> {
576 if self.is_some() { Some(self.0) } else { None }
577 }
578
579 #[allow(dead_code)]
580 pub fn is_some(&self) -> bool {
581 self.0 != num_traits::bounds::UpperBounded::max_value()
582 }
583
584 #[allow(dead_code)]
585 pub fn is_none(&self) -> bool {
586 self.0 == num_traits::bounds::UpperBounded::max_value()
587 }
588 }
589
590 impl From<Option<$index>> for $optional_index {
595 fn from(index: Option<$index>) -> Self {
596 if let Some(index) = index {
597 Self::new_some(index.0)
598 } else {
599 Self::new_none()
600 }
601 }
602 }
603
604 impl From<$index> for $optional_index {
605 fn from(index: $index) -> Self {
606 Self::new_some(index.0)
607 }
608 }
609
610 impl From<$optional_index> for Option<$index> {
611 fn from(optional_index: $optional_index) -> Self {
612 if optional_index.is_some() {
613 Some($index::new(optional_index.0))
614 } else {
615 None
616 }
617 }
618 }
619
620 impl From<usize> for $index {
625 fn from(value: usize) -> Self {
626 Self::new(
627 value
628 .try_into()
629 .ok()
630 .expect("index conversion from usize failed"),
631 )
632 }
633 }
634
635 impl From<usize> for $optional_index {
636 fn from(value: usize) -> Self {
637 Self::new_some(
638 value
639 .try_into()
640 .ok()
641 .expect("index conversion from usize failed"),
642 )
643 }
644 }
645
646 impl From<Option<usize>> for $optional_index {
647 fn from(value: Option<usize>) -> Self {
648 if let Some(value) = value {
649 Self::new_some(
650 value
651 .try_into()
652 .ok()
653 .expect("index conversion from usize failed"),
654 )
655 } else {
656 Self::new_none()
657 }
658 }
659 }
660
661 impl From<$index> for usize {
662 fn from(index: $index) -> Self {
663 index
664 .0
665 .try_into()
666 .ok()
667 .expect("index conversion to usize failed")
668 }
669 }
670
671 impl From<$optional_index> for Option<usize> {
672 fn from(index: $optional_index) -> Self {
673 if index.is_some() {
674 Some(
675 index
676 .0
677 .try_into()
678 .ok()
679 .expect("index conversion to usize failed"),
680 )
681 } else {
682 None
683 }
684 }
685 }
686
687 impl std::fmt::Debug for $index {
692 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693 write!(f, "{}({:?})", stringify!($index), self.0)
694 }
695 }
696
697 impl std::fmt::Debug for $optional_index {
698 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
699 if self.is_some() {
700 write!(f, "{}({:?})", stringify!($optional_index), self.0)
701 } else {
702 write!(f, "{}(None)", stringify!($optional_index))
703 }
704 }
705 }
706
707 impl std::fmt::Display for $index {
708 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
709 write!(f, "{}", self.0)
710 }
711 }
712
713 impl std::fmt::Display for $optional_index {
714 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
715 if self.is_some() {
716 write!(f, "{}", self.0)
717 } else {
718 write!(f, "None")
719 }
720 }
721 }
722
723 impl Clone for $index {
728 fn clone(&self) -> Self {
729 *self
730 }
731 }
732
733 impl Clone for $optional_index {
734 fn clone(&self) -> Self {
735 *self
736 }
737 }
738
739 impl Copy for $index {}
740
741 impl Copy for $optional_index {}
742
743 impl PartialEq for $index {
748 fn eq(&self, other: &Self) -> bool {
749 self.0.eq(&other.0)
750 }
751 }
752
753 impl PartialEq for $optional_index {
754 fn eq(&self, other: &Self) -> bool {
755 self.0.eq(&other.0)
756 }
757 }
758
759 impl Eq for $index {}
760
761 impl Eq for $optional_index {}
762
763 impl PartialOrd for $index {
768 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
769 Some(self.cmp(other))
770 }
771 }
772
773 impl PartialOrd for $optional_index {
774 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
775 Some(self.cmp(other))
776 }
777 }
778
779 impl Ord for $index {
780 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
781 self.0.cmp(&other.0)
782 }
783 }
784
785 impl Ord for $optional_index {
786 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
787 self.0.cmp(&other.0)
788 }
789 }
790
791 impl std::hash::Hash for $index {
796 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
797 self.0.hash(state);
798 }
799 }
800
801 impl std::hash::Hash for $optional_index {
802 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
803 self.0.hash(state);
804 }
805 }
806 };
807}