optional_numeric_index/
lib.rs1#[cfg(test)]
2mod tests;
3
4#[macro_export]
5macro_rules! implement_generic_index {
6 ($index:ident, $optional_index:ident) => {
7 pub struct $index<IndexType>(IndexType);
8
9 pub struct $optional_index<IndexType>(IndexType);
10
11 impl<IndexType> $index<IndexType> {
12 pub fn new(value: IndexType) -> Self
13 where
14 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
15 {
16 assert_ne!(value, IndexType::max_value());
17 Self(value)
18 }
19
20 pub fn from_usize(value: usize) -> Self
21 where
22 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
23 {
24 Self::new(
25 value
26 .try_into()
27 .ok()
28 .expect("index conversion from usize failed"),
29 )
30 }
31
32 pub fn into_inner(self) -> IndexType {
33 self.0
34 }
35 }
36
37 impl<IndexType> $optional_index<IndexType> {
38 pub fn new_some(value: IndexType) -> Self
39 where
40 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug,
41 {
42 assert_ne!(value, IndexType::max_value());
43 Self(value)
44 }
45
46 pub fn new_none() -> Self
47 where
48 IndexType: num_traits::bounds::UpperBounded,
49 {
50 Self(IndexType::max_value())
51 }
52
53 pub fn from_usize(value: usize) -> Self
54 where
55 IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug + TryFrom<usize>,
56 {
57 Self::new_some(
58 value
59 .try_into()
60 .ok()
61 .expect("index conversion from usize failed"),
62 )
63 }
64
65 pub fn into_inner(self) -> Option<IndexType>
66 where
67 IndexType: num_traits::bounds::UpperBounded + Eq,
68 {
69 if self.is_some() { Some(self.0) } else { None }
70 }
71
72 pub fn is_some(&self) -> bool
73 where
74 IndexType: num_traits::bounds::UpperBounded + Eq,
75 {
76 self.0 != IndexType::max_value()
77 }
78
79 pub fn is_none(&self) -> bool
80 where
81 IndexType: num_traits::bounds::UpperBounded + Eq,
82 {
83 self.0 == IndexType::max_value()
84 }
85 }
86
87 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
92 From<Option<$index<IndexType>>> for $optional_index<IndexType>
93 {
94 fn from(index: Option<$index<IndexType>>) -> Self {
95 if let Some(index) = index {
96 Self::new_some(index.0)
97 } else {
98 Self::new_none()
99 }
100 }
101 }
102
103 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
104 From<$index<IndexType>> for $optional_index<IndexType>
105 {
106 fn from(index: $index<IndexType>) -> Self {
107 Self::new_some(index.0)
108 }
109 }
110
111 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
112 From<$optional_index<IndexType>> for Option<$index<IndexType>>
113 {
114 fn from(optional_index: $optional_index<IndexType>) -> Self {
115 if optional_index.is_some() {
116 Some($index::new(optional_index.0))
117 } else {
118 None
119 }
120 }
121 }
122
123 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> From<IndexType>
124 for $index<IndexType>
125 {
126 fn from(value: IndexType) -> Self {
127 Self::new(value)
128 }
129 }
130
131 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> From<IndexType>
132 for $optional_index<IndexType>
133 {
134 fn from(value: IndexType) -> Self {
135 Self::new_some(value)
136 }
137 }
138
139 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug>
140 From<Option<IndexType>> for $optional_index<IndexType>
141 {
142 fn from(value: Option<IndexType>) -> Self {
143 match value {
144 Some(v) => Self::new_some(v),
145 None => Self::new_none(),
146 }
147 }
148 }
149
150 impl<IndexType: TryInto<usize>> From<$index<IndexType>> for usize {
155 fn from(index: $index<IndexType>) -> Self {
156 index
157 .0
158 .try_into()
159 .ok()
160 .expect("index conversion to usize failed")
161 }
162 }
163
164 impl<IndexType: num_traits::bounds::UpperBounded + Eq + TryInto<usize>>
165 From<$optional_index<IndexType>> for Option<usize>
166 {
167 fn from(index: $optional_index<IndexType>) -> Self {
168 if index.is_some() {
169 Some(
170 index
171 .0
172 .try_into()
173 .ok()
174 .expect("index conversion to usize failed"),
175 )
176 } else {
177 None
178 }
179 }
180 }
181
182 impl<IndexType: std::fmt::Debug> std::fmt::Debug for $index<IndexType> {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 write!(f, "{}({:?})", stringify!($index), self.0)
189 }
190 }
191
192 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Debug> std::fmt::Debug
193 for $optional_index<IndexType>
194 {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 if self.is_some() {
197 write!(f, "{}({:?})", stringify!($optional_index), self.0)
198 } else {
199 write!(f, "{}(None)", stringify!($optional_index))
200 }
201 }
202 }
203
204 impl<IndexType: std::fmt::Display> std::fmt::Display for $index<IndexType> {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 write!(f, "{}", self.0)
207 }
208 }
209
210 impl<IndexType: num_traits::bounds::UpperBounded + Eq + std::fmt::Display> std::fmt::Display
211 for $optional_index<IndexType>
212 {
213 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
214 if self.is_some() {
215 write!(f, "{}", self.0)
216 } else {
217 write!(f, "None")
218 }
219 }
220 }
221
222 impl<IndexType: Clone> Clone for $index<IndexType> {
227 fn clone(&self) -> Self {
228 Self(self.0.clone())
229 }
230 }
231
232 impl<IndexType: Clone> Clone for $optional_index<IndexType> {
233 fn clone(&self) -> Self {
234 Self(self.0.clone())
235 }
236 }
237
238 impl<IndexType: Copy> Copy for $index<IndexType> {}
239
240 impl<IndexType: Copy> Copy for $optional_index<IndexType> {}
241
242 impl<IndexType: PartialEq> PartialEq for $index<IndexType> {
247 fn eq(&self, other: &Self) -> bool {
248 self.0.eq(&other.0)
249 }
250 }
251
252 impl<IndexType: PartialEq> PartialEq for $optional_index<IndexType> {
253 fn eq(&self, other: &Self) -> bool {
254 self.0.eq(&other.0)
255 }
256 }
257
258 impl<IndexType: Eq> Eq for $index<IndexType> {}
259
260 impl<IndexType: Eq> Eq for $optional_index<IndexType> {}
261
262 impl<IndexType: PartialOrd> PartialOrd for $index<IndexType> {
267 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
268 self.0.partial_cmp(&other.0)
269 }
270 }
271
272 impl<IndexType: PartialOrd> PartialOrd for $optional_index<IndexType> {
273 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
274 self.0.partial_cmp(&other.0)
275 }
276 }
277
278 impl<IndexType: Ord> Ord for $index<IndexType> {
279 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
280 self.0.cmp(&other.0)
281 }
282 }
283
284 impl<IndexType: Ord> Ord for $optional_index<IndexType> {
285 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
286 self.0.cmp(&other.0)
287 }
288 }
289
290 impl<IndexType: std::hash::Hash> std::hash::Hash for $index<IndexType> {
295 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
296 self.0.hash(state);
297 }
298 }
299
300 impl<IndexType: std::hash::Hash> std::hash::Hash for $optional_index<IndexType> {
301 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
302 self.0.hash(state);
303 }
304 }
305 };
306}
307
308#[macro_export]
309macro_rules! implement_fixed_index {
310 ($index:ident, $optional_index:ident, $index_type:ty) => {
311 pub struct $index($index_type);
312
313 pub struct $optional_index($index_type);
314
315 impl $index {
316 pub fn new(value: $index_type) -> Self {
317 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
318 Self(value)
319 }
320
321 pub fn from_usize(value: usize) -> Self {
322 Self::new(
323 value
324 .try_into()
325 .ok()
326 .expect("index conversion from usize failed"),
327 )
328 }
329
330 pub fn into_inner(self) -> $index_type {
331 self.0
332 }
333 }
334
335 impl $optional_index {
336 pub fn new_some(value: $index_type) -> Self {
337 assert_ne!(value, num_traits::bounds::UpperBounded::max_value());
338 Self(value)
339 }
340
341 pub fn new_none() -> Self {
342 Self(num_traits::bounds::UpperBounded::max_value())
343 }
344
345 pub fn from_usize(value: usize) -> Self {
346 Self::new_some(
347 value
348 .try_into()
349 .ok()
350 .expect("index conversion from usize failed"),
351 )
352 }
353
354 pub fn into_inner(self) -> Option<$index_type> {
355 if self.is_some() { Some(self.0) } else { None }
356 }
357
358 pub fn is_some(&self) -> bool {
359 self.0 != num_traits::bounds::UpperBounded::max_value()
360 }
361
362 pub fn is_none(&self) -> bool {
363 self.0 == num_traits::bounds::UpperBounded::max_value()
364 }
365 }
366
367 impl From<Option<$index>> for $optional_index {
372 fn from(index: Option<$index>) -> Self {
373 if let Some(index) = index {
374 Self::new_some(index.0)
375 } else {
376 Self::new_none()
377 }
378 }
379 }
380
381 impl From<$index> for $optional_index {
382 fn from(index: $index) -> Self {
383 Self::new_some(index.0)
384 }
385 }
386
387 impl From<$optional_index> for Option<$index> {
388 fn from(optional_index: $optional_index) -> Self {
389 if optional_index.is_some() {
390 Some($index::new(optional_index.0))
391 } else {
392 None
393 }
394 }
395 }
396
397 impl From<$index_type> for $index {
398 fn from(value: $index_type) -> Self {
399 Self::new(value)
400 }
401 }
402
403 impl From<$index_type> for $optional_index {
404 fn from(value: $index_type) -> Self {
405 Self::new_some(value)
406 }
407 }
408
409 impl From<Option<$index_type>> for $optional_index {
410 fn from(value: Option<$index_type>) -> Self {
411 match value {
412 Some(v) => Self::new_some(v),
413 None => Self::new_none(),
414 }
415 }
416 }
417
418 impl From<$index> for usize {
423 fn from(index: $index) -> Self {
424 index
425 .0
426 .try_into()
427 .ok()
428 .expect("index conversion to usize failed")
429 }
430 }
431
432 impl From<$optional_index> for Option<usize> {
433 fn from(index: $optional_index) -> Self {
434 if index.is_some() {
435 Some(
436 index
437 .0
438 .try_into()
439 .ok()
440 .expect("index conversion to usize failed"),
441 )
442 } else {
443 None
444 }
445 }
446 }
447
448 impl std::fmt::Debug for $index {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 write!(f, "{}({:?})", stringify!($index), self.0)
455 }
456 }
457
458 impl std::fmt::Debug for $optional_index {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 if self.is_some() {
461 write!(f, "{}({:?})", stringify!($optional_index), self.0)
462 } else {
463 write!(f, "{}(None)", stringify!($optional_index))
464 }
465 }
466 }
467
468 impl std::fmt::Display for $index {
469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
470 write!(f, "{}", self.0)
471 }
472 }
473
474 impl std::fmt::Display for $optional_index {
475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476 if self.is_some() {
477 write!(f, "{}", self.0)
478 } else {
479 write!(f, "None")
480 }
481 }
482 }
483
484 impl Clone for $index {
489 fn clone(&self) -> Self {
490 Self(self.0.clone())
491 }
492 }
493
494 impl Clone for $optional_index {
495 fn clone(&self) -> Self {
496 Self(self.0.clone())
497 }
498 }
499
500 impl Copy for $index {}
501
502 impl Copy for $optional_index {}
503
504 impl PartialEq for $index {
509 fn eq(&self, other: &Self) -> bool {
510 self.0.eq(&other.0)
511 }
512 }
513
514 impl PartialEq for $optional_index {
515 fn eq(&self, other: &Self) -> bool {
516 self.0.eq(&other.0)
517 }
518 }
519
520 impl Eq for $index {}
521
522 impl Eq for $optional_index {}
523
524 impl PartialOrd for $index {
529 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
530 self.0.partial_cmp(&other.0)
531 }
532 }
533
534 impl PartialOrd for $optional_index {
535 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
536 self.0.partial_cmp(&other.0)
537 }
538 }
539
540 impl Ord for $index {
541 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
542 self.0.cmp(&other.0)
543 }
544 }
545
546 impl Ord for $optional_index {
547 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
548 self.0.cmp(&other.0)
549 }
550 }
551
552 impl std::hash::Hash for $index {
557 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
558 self.0.hash(state);
559 }
560 }
561
562 impl std::hash::Hash for $optional_index {
563 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
564 self.0.hash(state);
565 }
566 }
567 };
568}