index_vec/
macros.rs

1/// Generate the boilerplate for a newtyped index struct, for use with
2/// `IndexVec`.
3///
4/// In the future, if the compile-time overhead of doing so is reduced, this may
5/// be replaced with a proc macro.
6///
7/// ## Usage
8///
9/// ### Standard
10///
11/// The rough usage pattern of this macro is:
12///
13/// ```rust,no_run
14/// index_vec::define_index_type! {
15///     // Note that isn't actually a type alias, `MyIndex` is
16///     // actually defined as a struct. XXX is this too confusing?
17///     pub struct MyIndex = u32;
18///     // optional extra configuration here of the form:
19///     // `OPTION_NAME = stuff;`
20///     // See below for details.
21/// }
22/// ```
23///
24/// Note that you can use other index types than `u32`, and you can set it to be
25/// `MyIndex(pub u32)` as well. Currently, the wrapped item be a tuple struct,
26/// however (patches welcome).
27///
28/// ### Customization
29///
30/// After the struct declaration, there are a number of configuration options
31/// the macro uses to customize how the type it generates behaves. For example:
32///
33/// ```rust,no_run
34/// index_vec::define_index_type! {
35///     pub struct Span = u32;
36///
37///     // Don't allow any spans with values higher this.
38///     MAX_INDEX = 0x7fff_ff00;
39///
40///     // But I also am not too worried about it, so only
41///     // perform the asserts in debug builds.
42///     DISABLE_MAX_INDEX_CHECK = cfg!(not(debug_assertions));
43/// }
44/// ```
45///
46/// ## Configuration options
47///
48/// This macro has a few ways you can customize it's output behavior. There's
49/// not really any great syntax I can think of for them, but, well.
50///
51/// #### `MAX_INDEX = <expr producing usize>`
52///
53/// Assert if anything tries to construct an index above that value.
54///
55/// By default, this is `$raw_type::max_value() as usize`, e.g. we check that
56/// our cast from `usize` to our wrapper is lossless, but we assume any all
57/// instance of `$raw_type` is valid in this index domain.
58///
59/// Note that these tests can be disabled entirely, or conditionally, with
60/// `DISABLE_MAX_INDEX_CHECK`. Additionally, the generated type has
61/// `from_usize_unchecked` and `from_raw_unchecked` functions which can be used
62/// to ignore these checks.
63///
64/// #### `DISABLE_MAX_INDEX_CHECK = <expr>;`
65///
66/// Set to true to disable the assertions mentioned above. False by default.
67///
68/// To be clear, if this is set to false, we blindly assume all casts between
69/// `usize` and `$raw_type` succeed.
70///
71/// A common use is setting `DISABLE_MAX_INDEX_CHECK = !cfg!(debug_assertions)`
72/// to avoid the tests at compile time
73///
74/// For the sake of clarity, disabling this cannot lead to memory unsafety -- we
75/// still go through bounds checks when accessing slices, and no unsafe code
76/// should rely on on these checks (unless you write some, and don't! only use
77/// this for correctness!).
78///
79/// #### `DEFAULT = <expr>;`
80/// If provided, we'll implement `Default` for the index type using this
81/// expresson.
82///
83/// Example:
84///
85/// ```rust,no_run
86/// index_vec::define_index_type! {
87///     pub struct MyIdx = u16;
88///     MAX_INDEX = (u16::max_value() - 1) as usize;
89///     // Set the default index to be an invalid index, as
90///     // a hacky way of having this type behave somewhat
91///     // like it were an Option<MyIdx> without consuming
92///     // extra space.
93///     DEFAULT = (MyIdx::from_raw_unchecked(u16::max_value()));
94/// }
95/// ```
96///
97/// #### `DEBUG_FORMAT = <expr>;`
98///
99/// By default we write the underlying integer out in a Debug implementation
100/// with `{:?}`. Sometimes you'd like more info though. For example, the type of
101/// the index. This can be done via `DEBUG_FORMAT`:
102///
103/// ```rust
104/// index_vec::define_index_type! {
105///     struct FooIdx = usize;
106///     DEBUG_FORMAT = "Foo({})";
107/// }
108/// // Then ...
109/// # fn main() {
110/// let v = FooIdx::new(10);
111/// assert_eq!("Foo(10)", format!("{:?}", v));
112/// # }
113/// ```
114///
115/// #### `DISPLAY_FORMAT = <expr>;`
116///
117/// Similarly to `DEBUG_FORMAT`, we can implement Display for you. Unlike
118/// `DEBUG_FORMAT`, if you do not set this, we will not implement `Display` for
119/// the index type.
120///
121/// ```rust
122/// index_vec::define_index_type! {
123///     struct FooIdx = usize;
124///     DISPLAY_FORMAT = "{}";
125///     // Note that you can use both DEBUG_FORMAT and DISPLAY_FORMAT.
126///     DEBUG_FORMAT = "#<foo {}>";
127/// }
128/// // Then ...
129/// # fn main() {
130/// let v = FooIdx::new(10);
131/// assert_eq!("10", format!("{}", v));
132/// assert_eq!("#<foo 10>", format!("{:?}", v));
133/// # }
134/// ```
135///
136/// #### `IMPL_RAW_CONVERSIONS = true;`
137///
138/// We always automatically implement `From<usize> for YourIndex` and
139/// `From<YourIndex> for usize`. We don't do this for the "raw" type (e.g. `u32`
140/// if your type is declared as `struct FooIdx = u32;`), unless you request it
141/// via this option. It's an error to use this if your raw type is usize.
142///
143/// ```rust
144/// index_vec::define_index_type! {
145///     struct FooIdx = u32;
146///     IMPL_RAW_CONVERSIONS = true;
147/// }
148///
149/// # fn main() {
150/// let as_index = FooIdx::from(5u32);
151/// let as_u32 = u32::from(as_index);
152/// assert_eq!(as_u32, 5);
153/// # }
154/// ```
155#[macro_export]
156macro_rules! define_index_type {
157    // public api
158    (
159        $(#[$attrs:meta])*
160        $v:vis struct $type:ident = $raw:ident;
161        $($CONFIG_NAME:ident = $value:expr;)* $(;)?
162    ) => {
163        $crate::__define_index_type_inner!{
164            @configs [$(($CONFIG_NAME; $value))*]
165            @attrs [$(#[$attrs])*]
166            @derives [#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]]
167            @decl [$v struct $type ($raw)]
168            @debug_fmt ["{}"]
169            @max [(<$raw>::max_value() as usize)]
170            @no_check_max [false]
171        }
172    };
173}
174
175#[macro_export]
176#[doc(hidden)]
177macro_rules! unknown_define_index_type_option {
178    () => {};
179}
180
181#[cfg(feature = "serde")]
182#[macro_export]
183#[doc(hidden)]
184macro_rules! __internal_maybe_index_impl_serde {
185    ($type:ident) => {
186        impl serde::ser::Serialize for $type {
187            fn serialize<S: serde::ser::Serializer>(
188                &self,
189                serializer: S,
190            ) -> Result<S::Ok, S::Error> {
191                self.index().serialize(serializer)
192            }
193        }
194
195        impl<'de> serde::de::Deserialize<'de> for $type {
196            fn deserialize<D: serde::de::Deserializer<'de>>(
197                deserializer: D,
198            ) -> Result<Self, D::Error> {
199                usize::deserialize(deserializer).map(Self::from_usize)
200            }
201        }
202    };
203}
204
205#[cfg(not(feature = "serde"))]
206#[macro_export]
207#[doc(hidden)]
208macro_rules! __internal_maybe_index_impl_serde {
209    ($type:ident) => {};
210}
211
212#[cfg(feature = "rkyv")]
213#[macro_export]
214#[doc(hidden)]
215macro_rules! __internal_maybe_index_impl_rkyv {
216    ($type:ident) => {
217        impl rkyv::Archive for $type {
218            type Archived = <usize as rkyv::Archive>::Archived;
219            type Resolver = <usize as rkyv::Archive>::Resolver;
220
221            #[inline]
222            unsafe fn resolve(
223                &self,
224                pos: usize,
225                resolver: Self::Resolver,
226                out: *mut Self::Archived,
227            ) {
228                self.index().resolve(pos, resolver, out)
229            }
230        }
231
232        impl<S: rkyv::ser::ScratchSpace + rkyv::ser::Serializer + ?Sized> rkyv::Serialize<S>
233            for $type
234        {
235            #[inline]
236            fn serialize(
237                &self,
238                serializer: &mut S,
239            ) -> Result<Self::Resolver, <S as rkyv::Fallible>::Error> {
240                self.index().serialize(serializer)
241            }
242        }
243
244        impl<D: rkyv::Fallible + ?Sized> rkyv::Deserialize<$type, D>
245            for rkyv::Archived<rkyv::FixedUsize>
246        {
247            #[inline]
248            fn deserialize(&self, deserializer: &mut D) -> Result<$type, D::Error> {
249                let index = rkyv::Archived::<rkyv::FixedUsize>::deserialize(self, deserializer)?;
250
251                Ok($type::from_usize(index))
252            }
253        }
254    };
255}
256
257#[cfg(not(feature = "rkyv"))]
258#[macro_export]
259#[doc(hidden)]
260macro_rules! __internal_maybe_index_impl_rkyv {
261    ($type:ident) => {};
262}
263
264#[macro_export]
265#[doc(hidden)]
266macro_rules! __define_index_type_inner {
267    // DISABLE_MAX_INDEX_CHECK
268    (
269        @configs [(DISABLE_MAX_INDEX_CHECK; $no_check_max:expr) $(($CONFIG_NAME:ident; $value:expr))*]
270        @attrs [$(#[$attrs:meta])*]
271        @derives [$(#[$derive:meta])*]
272        @decl [$v:vis struct $type:ident ($raw:ident)]
273        @debug_fmt [$dbg:expr]
274        @max [$max:expr]
275        @no_check_max [$_old_no_check_max:expr]
276    ) => {
277        $crate::__define_index_type_inner!{
278            @configs [$(($CONFIG_NAME; $value))*]
279            @attrs [$(#[$attrs])*]
280            @derives [$(#[$derive])*]
281            @decl [$v struct $type ($raw)]
282            @debug_fmt [$dbg]
283            @max [$max]
284            @no_check_max [$no_check_max]
285        }
286    };
287
288    // MAX_INDEX
289    (
290        @configs [(MAX_INDEX; $new_max:expr) $(($CONFIG_NAME:ident; $value:expr))*]
291        @attrs [$(#[$attrs:meta])*]
292        @derives [$(#[$derive:meta])*]
293        @decl [$v:vis struct $type:ident ($raw:ident)]
294        @debug_fmt [$dbg:expr]
295        @max [$max:expr]
296        @no_check_max [$cm:expr]
297    ) => {
298        $crate::__define_index_type_inner!{
299            @configs [$(($CONFIG_NAME; $value))*]
300            @attrs [$(#[$attrs])*]
301            @derives [$(#[$derive])*]
302            @decl [$v struct $type ($raw)]
303            @debug_fmt [$dbg]
304            @max [$new_max]
305            @no_check_max [$cm]
306        }
307    };
308
309    // DEFAULT
310    (
311        @configs [(DEFAULT; $default_expr:expr) $(($CONFIG_NAME:ident; $value:expr))*]
312        @attrs [$(#[$attrs:meta])*]
313        @derives [$(#[$derive:meta])*]
314        @decl [$v:vis struct $type:ident ($raw:ident)]
315        @debug_fmt [$dbg:expr]
316        @max [$max:expr]
317        @no_check_max [$no_check_max:expr]
318    ) => {
319        $crate::__define_index_type_inner!{
320            @configs [$(($CONFIG_NAME; $value))*]
321            @attrs [$(#[$attrs])*]
322            @derives [$(#[$derive])*]
323            @decl [$v struct $type ($raw)]
324            @debug_fmt [$dbg]
325            @max [$max]
326            @no_check_max [$no_check_max]
327        }
328        impl Default for $type {
329            #[inline]
330            fn default() -> Self {
331                $default_expr
332            }
333        }
334    };
335
336    // DEBUG_FORMAT
337    (
338        @configs [(DEBUG_FORMAT; $dbg:expr) $(($CONFIG_NAME:ident; $value:expr))*]
339        @attrs [$(#[$attrs:meta])*]
340        @derives [$(#[$derive:meta])*]
341        @decl [$v:vis struct $type:ident ($raw:ident)]
342        @debug_fmt [$old_dbg:expr]
343        @max [$max:expr]
344        @no_check_max [$no_check_max:expr]
345    ) => {
346        $crate::__define_index_type_inner!{
347            @configs [$(($CONFIG_NAME; $value))*]
348            @attrs [$(#[$attrs])*]
349            @derives [$(#[$derive])*]
350            @decl [$v struct $type ($raw)]
351            @debug_fmt [$dbg]
352            @max [$max]
353            @no_check_max [$no_check_max]
354        }
355    };
356
357    // DISPLAY_FORMAT
358    (
359        @configs [(DISPLAY_FORMAT; $format:expr) $(($CONFIG_NAME:ident; $value:expr))*]
360        @attrs [$(#[$attrs:meta])*]
361        @derives [$(#[$derive:meta])*]
362        @decl [$v:vis struct $type:ident ($raw:ident)]
363        @debug_fmt [$dbg:expr]
364        @max [$max:expr]
365        @no_check_max [$no_check_max:expr]
366    ) => {
367        $crate::__define_index_type_inner!{
368            @configs [$(($CONFIG_NAME; $value))*]
369            @attrs [$(#[$attrs])*]
370            @derives [$(#[$derive])*]
371            @decl [$v struct $type ($raw)]
372            @debug_fmt [$dbg]
373            @max [$max]
374            @no_check_max [$no_check_max]
375        }
376
377        impl core::fmt::Display for $type {
378            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
379                write!(f, $format, self.index())
380            }
381        }
382    };
383
384    // IMPL_RAW_CONVERSIONS
385    (
386        @configs [(IMPL_RAW_CONVERSIONS; $val:expr) $(($CONFIG_NAME:ident; $value:expr))*]
387        @attrs [$(#[$attrs:meta])*]
388        @derives [$(#[$derive:meta])*]
389        @decl [$v:vis struct $type:ident ($raw:ident)]
390        @debug_fmt [$dbg:expr]
391        @max [$max:expr]
392        @no_check_max [$no_check_max:expr]
393    ) => {
394        $crate::__define_index_type_inner!{
395            @configs [$(($CONFIG_NAME; $value))*]
396            @attrs [$(#[$attrs])*]
397            @derives [$(#[$derive])*]
398            @decl [$v struct $type ($raw)]
399            @debug_fmt [$dbg]
400            @max [$max]
401            @no_check_max [$no_check_max]
402        }
403        // Ensure they passed in true. This is... cludgey.
404        const _: [(); 1] = [(); $val as usize];
405
406        impl From<$type> for $raw {
407            #[inline]
408            fn from(v: $type) -> $raw {
409                v.raw()
410            }
411        }
412
413        impl From<$raw> for $type {
414            #[inline]
415            fn from(value: $raw) -> Self {
416                Self::from_raw(value)
417            }
418        }
419    };
420    // Try to make rust emit a decent error message...
421    (
422        @configs [($other:ident; $format:expr) $(($CONFIG_NAME:ident; $value:expr))*]
423        @attrs [$(#[$attrs:meta])*]
424        @derives [$(#[$derive:meta])*]
425        @decl [$v:vis struct $type:ident ($raw:ident)]
426        @debug_fmt [$dbg:expr]
427        @max [$max:expr]
428        @no_check_max [$no_check_max:expr]
429    ) => {
430        $crate::unknown_define_index_type_option!($other);
431    };
432    // finish
433    (
434        @configs []
435        @attrs [$(#[$attrs:meta])*]
436        @derives [$(#[$derive:meta])*]
437        @decl [$v:vis struct $type:ident ($raw:ident)]
438        @debug_fmt [$dbg:expr]
439        @max [$max:expr]
440        @no_check_max [$no_check_max:expr]
441    ) => {
442
443        $(#[$derive])*
444        $(#[$attrs])*
445        #[repr(transparent)]
446        $v struct $type { _raw: $raw }
447
448        impl $type {
449            /// If `Self::CHECKS_MAX_INDEX` is true, we'll assert if trying to
450            /// produce a value larger than this in any of the ctors that don't
451            /// have `unchecked` in their name.
452            $v const MAX_INDEX: usize = $max;
453
454            /// Does this index type assert if asked to construct an index
455            /// larger than MAX_INDEX?
456            $v const CHECKS_MAX_INDEX: bool = !$no_check_max;
457
458            /// Construct this index type from a usize. Alias for `from_usize`.
459            #[inline(always)]
460            $v fn new(value: usize) -> Self {
461                Self::from_usize(value)
462            }
463
464            /// Construct this index type from the wrapped integer type.
465            #[inline(always)]
466            $v fn from_raw(value: $raw) -> Self {
467                Self::from_usize(value as usize)
468            }
469
470            /// Construct this index type from one in a different domain
471            #[inline(always)]
472            $v fn from_foreign<F: $crate::Idx>(value: F) -> Self {
473                Self::from_usize(value.index())
474            }
475
476            /// Construct from a usize without any checks.
477            #[inline(always)]
478            $v const fn from_usize_unchecked(value: usize) -> Self {
479                Self { _raw: value as $raw }
480            }
481
482            /// Construct from the underlying type without any checks.
483            #[inline(always)]
484            $v const fn from_raw_unchecked(raw: $raw) -> Self {
485                Self { _raw: raw }
486            }
487
488            /// Construct this index type from a usize.
489            #[inline]
490            $v fn from_usize(value: usize) -> Self {
491                Self::check_index(value as usize);
492                Self { _raw: value as $raw }
493            }
494
495            /// Get the wrapped index as a usize.
496            #[inline(always)]
497            $v const fn index(self) -> usize {
498                self._raw as usize
499            }
500
501            /// Get the wrapped index.
502            #[inline(always)]
503            $v const fn raw(self) -> $raw {
504                self._raw
505            }
506
507            /// Asserts `v <= Self::MAX_INDEX` unless Self::CHECKS_MAX_INDEX is false.
508            #[inline]
509            $v fn check_index(v: usize) {
510                if Self::CHECKS_MAX_INDEX && (v > Self::MAX_INDEX) {
511                    $crate::__max_check_fail(v, Self::MAX_INDEX);
512                }
513            }
514
515            const _ENSURE_RAW_IS_UNSIGNED: [(); 0] = [(); <$raw>::MIN as usize];
516        }
517
518        impl core::fmt::Debug for $type {
519            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
520                write!(f, $dbg, self.index())
521            }
522        }
523
524        impl core::cmp::PartialOrd<usize> for $type {
525            #[inline]
526            fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
527                self.index().partial_cmp(other)
528            }
529        }
530
531        impl core::cmp::PartialOrd<$type> for usize {
532            #[inline]
533            fn partial_cmp(&self, other: &$type) -> Option<core::cmp::Ordering> {
534                self.partial_cmp(&other.index())
535            }
536        }
537
538        impl PartialEq<usize> for $type {
539            #[inline]
540            fn eq(&self, other: &usize) -> bool {
541                self.index() == *other
542            }
543        }
544
545        impl PartialEq<$type> for usize {
546            #[inline]
547            fn eq(&self, other: &$type) -> bool {
548                *self == other.index()
549            }
550        }
551
552        impl core::ops::Add<usize> for $type {
553            type Output = Self;
554            #[inline]
555            fn add(self, other: usize) -> Self {
556                // use wrapping add so that it's up to the index type whether or
557                // not to check -- e.g. if checks are disabled, they're disabled
558                // on both debug and release.
559                Self::new(self.index().wrapping_add(other))
560            }
561        }
562
563        impl core::ops::Sub<usize> for $type {
564            type Output = Self;
565            #[inline]
566            fn sub(self, other: usize) -> Self {
567                // use wrapping sub so that it's up to the index type whether or
568                // not to check -- e.g. if checks are disabled, they're disabled
569                // on both debug and release.
570                Self::new(self.index().wrapping_sub(other))
571            }
572        }
573
574        impl core::ops::AddAssign<usize> for $type {
575            #[inline]
576            fn add_assign(&mut self, other: usize) {
577                *self = *self + other
578            }
579        }
580
581        impl core::ops::SubAssign<usize> for $type {
582            #[inline]
583            fn sub_assign(&mut self, other: usize) {
584                *self = *self - other;
585            }
586        }
587
588        impl core::ops::Rem<usize> for $type {
589            type Output = Self;
590            #[inline]
591            fn rem(self, other: usize) -> Self {
592                Self::new(self.index() % other)
593            }
594        }
595
596        impl core::ops::Add<$type> for usize {
597            type Output = $type;
598            #[inline]
599            fn add(self, other: $type) -> $type {
600                other + self
601            }
602        }
603
604        impl core::ops::Sub<$type> for usize {
605            type Output = $type;
606            #[inline]
607            fn sub(self, other: $type) -> $type {
608                $type::new(self.wrapping_sub(other.index()))
609            }
610        }
611
612        impl core::ops::Add for $type {
613            type Output = $type;
614            #[inline]
615            fn add(self, other: $type) -> $type {
616                $type::new(other.index() + self.index())
617            }
618        }
619
620        impl core::ops::Sub for $type {
621            type Output = $type;
622            #[inline]
623            fn sub(self, other: $type) -> $type {
624                $type::new(self.index().wrapping_sub(other.index()))
625            }
626        }
627
628        impl core::ops::AddAssign for $type {
629            #[inline]
630            fn add_assign(&mut self, other: $type) {
631                *self = *self + other
632            }
633        }
634
635        impl core::ops::SubAssign for $type {
636            #[inline]
637            fn sub_assign(&mut self, other: $type) {
638                *self = *self - other;
639            }
640        }
641
642        impl $crate::Idx for $type {
643            #[inline]
644            fn from_usize(value: usize) -> Self {
645                Self::from(value)
646            }
647
648            #[inline]
649            fn index(self) -> usize {
650                usize::from(self)
651            }
652        }
653
654        impl From<$type> for usize {
655            #[inline]
656            fn from(v: $type) -> usize {
657                v.index()
658            }
659        }
660
661        impl From<usize> for $type {
662            #[inline]
663            fn from(value: usize) -> Self {
664                $type::from_usize(value)
665            }
666        }
667
668        $crate::__internal_maybe_index_impl_serde!($type);
669        $crate::__internal_maybe_index_impl_rkyv!($type);
670    };
671}