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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}