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_inner(self) -> IndexType {
72 self.0
73 }
74 }
75
76 impl<IndexType> $optional_index<IndexType> {
77 #[allow(dead_code)]
78 pub fn new_some(value: IndexType) -> Self
79 where
80 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
81 {
82 assert_ne!(value, IndexType::max_value());
83 Self(value)
84 }
85
86 #[allow(dead_code)]
87 pub fn new_none() -> Self
88 where
89 IndexType: num_traits::bounds::UpperBounded,
90 {
91 Self(IndexType::max_value())
92 }
93
94 #[allow(dead_code)]
95 pub fn from_usize(value: usize) -> Self
96 where
97 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
98 {
99 Self::new_some(
100 value
101 .try_into()
102 .ok()
103 .expect("index conversion from usize failed"),
104 )
105 }
106
107 #[allow(dead_code)]
108 pub fn into_inner(self) -> Option<IndexType>
109 where
110 IndexType: num_traits::bounds::UpperBounded + Eq,
111 {
112 if self.is_some() { Some(self.0) } else { None }
113 }
114
115 #[allow(dead_code)]
116 pub fn is_some(&self) -> bool
117 where
118 IndexType: num_traits::bounds::UpperBounded + Eq,
119 {
120 self.0 != IndexType::max_value()
121 }
122
123 #[allow(dead_code)]
124 pub fn is_none(&self) -> bool
125 where
126 IndexType: num_traits::bounds::UpperBounded + Eq,
127 {
128 self.0 == IndexType::max_value()
129 }
130 }
131
132 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
137 From<Option<$index<IndexType>>> for $optional_index<IndexType>
138 {
139 fn from(index: Option<$index<IndexType>>) -> Self {
140 if let Some(index) = index {
141 Self::new_some(index.0)
142 } else {
143 Self::new_none()
144 }
145 }
146 }
147
148 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
149 From<$index<IndexType>> for $optional_index<IndexType>
150 {
151 fn from(index: $index<IndexType>) -> Self {
152 Self::new_some(index.0)
153 }
154 }
155
156 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
157 From<$optional_index<IndexType>> for Option<$index<IndexType>>
158 {
159 fn from(optional_index: $optional_index<IndexType>) -> Self {
160 if optional_index.is_some() {
161 Some($index::new(optional_index.0))
162 } else {
163 None
164 }
165 }
166 }
167
168 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> From<IndexType>
169 for $index<IndexType>
170 {
171 fn from(value: IndexType) -> Self {
172 Self::new(value)
173 }
174 }
175
176 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> From<IndexType>
177 for $optional_index<IndexType>
178 {
179 fn from(value: IndexType) -> Self {
180 Self::new_some(value)
181 }
182 }
183
184 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
185 From<Option<IndexType>> for $optional_index<IndexType>
186 {
187 fn from(value: Option<IndexType>) -> Self {
188 match value {
189 Some(v) => Self::new_some(v),
190 None => Self::new_none(),
191 }
192 }
193 }
194
195 impl<IndexType: TryInto<usize>> From<$index<IndexType>> for usize {
200 fn from(index: $index<IndexType>) -> Self {
201 index
202 .0
203 .try_into()
204 .ok()
205 .expect("index conversion to usize failed")
206 }
207 }
208
209 impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
210 From<$optional_index<IndexType>> for Option<usize>
211 {
212 fn from(index: $optional_index<IndexType>) -> Self {
213 if index.is_some() {
214 Some(
215 index
216 .0
217 .try_into()
218 .ok()
219 .expect("index conversion to usize failed"),
220 )
221 } else {
222 None
223 }
224 }
225 }
226
227 impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 write!(f, "{}({:?})", stringify!($index), self.0)
234 }
235 }
236
237 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
238 for $optional_index<IndexType>
239 {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 if self.is_some() {
242 write!(f, "{}({:?})", stringify!($optional_index), self.0)
243 } else {
244 write!(f, "{}(None)", stringify!($optional_index))
245 }
246 }
247 }
248
249 impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 write!(f, "{}", self.0)
252 }
253 }
254
255 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
256 for $optional_index<IndexType>
257 {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 if self.is_some() {
260 write!(f, "{}", self.0)
261 } else {
262 write!(f, "None")
263 }
264 }
265 }
266
267 impl<IndexType: Clone> Clone for $index<IndexType> {
272 fn clone(&self) -> Self {
273 Self(self.0.clone())
274 }
275 }
276
277 impl<IndexType: Clone> Clone for $optional_index<IndexType> {
278 fn clone(&self) -> Self {
279 Self(self.0.clone())
280 }
281 }
282
283 impl<IndexType: Copy> Copy for $index<IndexType> {}
284
285 impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
286
287 impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
292 fn eq(&self, other: &Self) -> bool {
293 self.0.eq(&other.0)
294 }
295 }
296
297 impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
298 fn eq(&self, other: &Self) -> bool {
299 self.0.eq(&other.0)
300 }
301 }
302
303 impl<IndexType: Eq> Eq for $index<IndexType> {}
304
305 impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
306
307 impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
312 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
313 self.0.partial_cmp(&other.0)
314 }
315 }
316
317 impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
318 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
319 self.0.partial_cmp(&other.0)
320 }
321 }
322
323 impl<IndexType: Ord> Ord for $index<IndexType> {
324 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
325 self.0.cmp(&other.0)
326 }
327 }
328
329 impl<IndexType: Ord> Ord for $optional_index<IndexType> {
330 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
331 self.0.cmp(&other.0)
332 }
333 }
334
335 impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
340 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
341 self.0.hash(state);
342 }
343 }
344
345 impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
346 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
347 self.0.hash(state);
348 }
349 }
350 };
351}
352
353#[macro_export]
354macro_rules! implement_fixed_index {
355 ($index:ident, $optional_index:ident, $index_type:ty) => {
356 struct $index($index_type);
357
358 struct $optional_index($index_type);
359
360 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
361 };
362
363 (pub $index:ident, pub $optional_index:ident, $index_type:ty) => {
364 pub struct $index($index_type);
365
366 pub struct $optional_index($index_type);
367
368 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
369 };
370
371 (pub(crate) $index:ident, pub(crate) $optional_index:ident, $index_type:ty) => {
372 pub(crate) struct $index($index_type);
373
374 pub(crate) struct $optional_index($index_type);
375
376 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
377 };
378
379 (pub(super) $index:ident, pub(super) $optional_index:ident, $index_type:ty) => {
380 pub(super) struct $index($index_type);
381
382 pub(super) struct $optional_index($index_type);
383
384 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
385 };
386
387 (pub(in $index_visibility:path) $index:ident, pub(in $optional_index_visibility:path) $optional_index:ident, $index_type:ty) => {
388 pub(in $index_visibility) struct $index($index_type);
389
390 pub(in $optional_index_visibility) struct $optional_index($index_type);
391
392 implement_fixed_index!($index, $optional_index, $index_type, __inner__);
393 };
394
395 ($index:ident, $optional_index:ident, $index_type:ty, __inner__) => {
396 impl $index {
397 #[allow(dead_code)]
398 pub fn new(value: $index_type) -> Self {
399 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
400 Self(value)
401 }
402
403 #[allow(dead_code)]
404 pub fn from_usize(value: usize) -> Self {
405 Self::new(
406 value
407 .try_into()
408 .ok()
409 .expect("index conversion from usize failed"),
410 )
411 }
412
413 #[allow(dead_code)]
414 pub fn into_inner(self) -> $index_type {
415 self.0
416 }
417 }
418
419 impl $optional_index {
420 #[allow(dead_code)]
421 pub fn new_some(value: $index_type) -> Self {
422 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
423 Self(value)
424 }
425
426 #[allow(dead_code)]
427 pub fn new_none() -> Self {
428 Self(num_traits::bounds::UpperBounded::max_value())
429 }
430
431 #[allow(dead_code)]
432 pub fn from_usize(value: usize) -> Self {
433 Self::new_some(
434 value
435 .try_into()
436 .ok()
437 .expect("index conversion from usize failed"),
438 )
439 }
440
441 #[allow(dead_code)]
442 pub fn into_inner(self) -> Option<$index_type> {
443 if self.is_some() { Some(self.0) } else { None }
444 }
445
446 #[allow(dead_code)]
447 pub fn is_some(&self) -> bool {
448 self.0 != num_traits::bounds::UpperBounded::max_value()
449 }
450
451 #[allow(dead_code)]
452 pub fn is_none(&self) -> bool {
453 self.0 == num_traits::bounds::UpperBounded::max_value()
454 }
455 }
456
457 impl From<Option<$index>> for $optional_index {
462 fn from(index: Option<$index>) -> Self {
463 if let Some(index) = index {
464 Self::new_some(index.0)
465 } else {
466 Self::new_none()
467 }
468 }
469 }
470
471 impl From<$index> for $optional_index {
472 fn from(index: $index) -> Self {
473 Self::new_some(index.0)
474 }
475 }
476
477 impl From<$optional_index> for Option<$index> {
478 fn from(optional_index: $optional_index) -> Self {
479 if optional_index.is_some() {
480 Some($index::new(optional_index.0))
481 } else {
482 None
483 }
484 }
485 }
486
487 impl From<$index_type> for $index {
488 fn from(value: $index_type) -> Self {
489 Self::new(value)
490 }
491 }
492
493 impl From<$index_type> for $optional_index {
494 fn from(value: $index_type) -> Self {
495 Self::new_some(value)
496 }
497 }
498
499 impl From<Option<$index_type>> for $optional_index {
500 fn from(value: Option<$index_type>) -> Self {
501 match value {
502 Some(v) => Self::new_some(v),
503 None => Self::new_none(),
504 }
505 }
506 }
507
508 impl From<$index> for usize {
513 fn from(index: $index) -> Self {
514 index
515 .0
516 .try_into()
517 .ok()
518 .expect("index conversion to usize failed")
519 }
520 }
521
522 impl From<$optional_index> for Option<usize> {
523 fn from(index: $optional_index) -> Self {
524 if index.is_some() {
525 Some(
526 index
527 .0
528 .try_into()
529 .ok()
530 .expect("index conversion to usize failed"),
531 )
532 } else {
533 None
534 }
535 }
536 }
537
538 impl std::fmt::Debug for $index {
543 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
544 write!(f, "{}({:?})", stringify!($index), self.0)
545 }
546 }
547
548 impl std::fmt::Debug for $optional_index {
549 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
550 if self.is_some() {
551 write!(f, "{}({:?})", stringify!($optional_index), self.0)
552 } else {
553 write!(f, "{}(None)", stringify!($optional_index))
554 }
555 }
556 }
557
558 impl std::fmt::Display for $index {
559 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560 write!(f, "{}", self.0)
561 }
562 }
563
564 impl std::fmt::Display for $optional_index {
565 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 if self.is_some() {
567 write!(f, "{}", self.0)
568 } else {
569 write!(f, "None")
570 }
571 }
572 }
573
574 impl Clone for $index {
579 fn clone(&self) -> Self {
580 *self
581 }
582 }
583
584 impl Clone for $optional_index {
585 fn clone(&self) -> Self {
586 *self
587 }
588 }
589
590 impl Copy for $index {}
591
592 impl Copy for $optional_index {}
593
594 impl PartialEq for $index {
599 fn eq(&self, other: &Self) -> bool {
600 self.0.eq(&other.0)
601 }
602 }
603
604 impl PartialEq for $optional_index {
605 fn eq(&self, other: &Self) -> bool {
606 self.0.eq(&other.0)
607 }
608 }
609
610 impl Eq for $index {}
611
612 impl Eq for $optional_index {}
613
614 impl PartialOrd for $index {
619 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
620 Some(self.cmp(other))
621 }
622 }
623
624 impl PartialOrd for $optional_index {
625 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
626 Some(self.cmp(other))
627 }
628 }
629
630 impl Ord for $index {
631 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
632 self.0.cmp(&other.0)
633 }
634 }
635
636 impl Ord for $optional_index {
637 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
638 self.0.cmp(&other.0)
639 }
640 }
641
642 impl std::hash::Hash for $index {
647 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
648 self.0.hash(state);
649 }
650 }
651
652 impl std::hash::Hash for $optional_index {
653 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
654 self.0.hash(state);
655 }
656 }
657 };
658}