Skip to main content

scattered_collect/
lib.rs

1#![doc = include_str!("../README.md")]
2
3pub mod hash;
4pub mod iterable;
5pub mod map;
6pub mod referenced_slice;
7pub mod set;
8pub mod slice;
9pub mod sorted_referenced_slice;
10pub mod sorted_slice;
11
12pub use iterable::ScatteredIterable;
13pub use map::ScatteredMap;
14pub use referenced_slice::ScatteredReferencedSlice;
15pub use set::ScatteredSet;
16pub use slice::ScatteredSlice;
17pub use sorted_referenced_slice::ScatteredSortedReferencedSlice;
18pub use sorted_slice::ScatteredSortedSlice;
19
20#[doc(hidden)]
21/// The per-record storage type of a scattered collection.
22pub trait ScatteredElementType {
23    /// The per-record type stored in the collection's link section.
24    type T;
25}
26
27#[doc(hidden)]
28/// The two-component types of a tuple-shaped scattered collection.
29pub trait ScatteredElementTuple {
30    /// The first component (key) type.
31    type A;
32    /// The second component (value) type.
33    type B;
34}
35
36#[doc(hidden)]
37#[macro_export]
38macro_rules! __scatter_parse {
39    // Handle a `crate_path = ...` override at the call site: the proc-macro
40    // form already applied it to the support path reaching this macro, so strip
41    // it and continue with the collection.
42    (#[scatter (crate_path = $cp:path , $($meta:tt)*)] $($rest:tt)*) => {
43        $crate::__support::scatter_parse!(#[scatter($($meta)*)] $($rest)*);
44    };
45
46    // Send the #[scatter]'d item into the collection's private macro.
47    (#[scatter ($($meta:tt)*)] $(#[$imeta:meta])* $vis:vis static $($item:tt)*) => {
48        $($meta)* ! (
49            ([$($meta)*] =>
50            $(#[$imeta])*
51            $vis static
52            $($item)*
53            )
54        );
55    };
56    (#[scatter ($($meta:tt)*)] $(#[$imeta:meta])* $vis:vis const $($item:tt)*) => {
57        $($meta)* ! (
58            ([$($meta)*] =>
59            $(#[$imeta])*
60            $vis const
61            $($item)*
62            )
63        );
64    };
65    (#[scatter] $($rest:tt)* ) => {
66        compile_error!("Missing collection for #[scatter] attribute. Expected: #[scatter(COLLECTION)] <item>;");
67    };
68
69    (@reorder (#[scatter] $($item:tt)*) ($($rest:tt)*)) => {
70        $crate::__support::scatter_parse!(#[scatter] $($rest)* $($item)*);
71    };
72    (@reorder (#[$top:meta] $($item:tt)*) ($($rest:tt)*)) => {
73        $crate::__support::scatter_parse!(@reorder($($item)*) (#[$top] $($rest)*));
74    };
75    (@reorder ($item:item;) $($rest:tt)*) => {
76        compile_error!("Missing #[scatter] attribute.");
77    };
78    (@reorder $($rest:tt)*) => {
79        compile_error!("Missing #[scatter] attribute.");
80    };
81
82    ($($rest:tt)*) => {
83        $crate::__support::scatter_parse!(@reorder ($($rest)*) ());
84    };
85}
86
87#[doc(hidden)]
88#[macro_export]
89macro_rules! __gather_parse {
90    // Send the #[gather]'d item into the collection's private macro.
91
92    (@dispatch $macro:ident $(#[$imeta:meta])* $vis:vis static $name:ident $($rest:tt)* ) => {
93        $crate::__support::combine!(output=ident prefix=($crate::__gather_parse!) paren=() paren_prefix=(@unique ) input=(
94            H
95            __LOCATIONHASH__(of=($(#[$imeta])* $vis static $name $($rest)*))
96        ) paren_suffix=($macro $(#[$imeta])* $vis static $name $($rest)*) suffix=(;));
97    };
98
99    (@unique $unique:ident $macro:ident $(#[$imeta:meta])* $vis:vis static $name:ident: ($($collection:tt)*) ($($ty:tt)*);) => {
100        $crate::$macro!(@gather $unique $(#[$imeta])* $vis static $name: ($($collection)*) <$($ty)*>;);
101
102        $crate::__support::combine!(output=ident prefix=(#[doc(hidden)] #[macro_export] macro_rules!) input=(__ $name __ $macro __ $unique) suffix=({
103            ($passthru:tt) => {
104                $crate::$macro!(@scatter [$name :: $unique] $passthru);
105            };
106        }));
107
108        $crate::__support::combine!(output=ident prefix=(#[doc(hidden)] $vis use) input=(__ $name __ $macro __ $unique) suffix=(as $name;));
109    };
110
111    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredSlice < $ty:ty >; ) => {
112        $crate::__support::gather_parse!(@dispatch __slice $(#[$imeta])* $vis static $name: ($($collection)*) ( $ty ););
113    };
114
115    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredSortedSlice < $ty:ty >; ) => {
116        $crate::__support::gather_parse!(@dispatch __sorted_slice $(#[$imeta])* $vis static $name: ($($collection)*) ( $ty ););
117    };
118
119    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredReferencedSlice < $ty:ty >; ) => {
120        $crate::__support::gather_parse!(@dispatch __referenced_slice $(#[$imeta])* $vis static $name: ($($collection)*) ( $ty ););
121    };
122
123    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredSortedReferencedSlice < $ty:ty >; ) => {
124        $crate::__support::gather_parse!(@dispatch __sorted_referenced_slice $(#[$imeta])* $vis static $name: ($($collection)*) ( $ty ););
125    };
126
127    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredMap < $key:ty, $value:ty >; ) => {
128        $crate::__support::gather_parse!(@dispatch __map $(#[$imeta])* $vis static $name: ($($collection)*) ( $key, $value ););
129    };
130
131    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredSet < $key:ty >; ) => {
132        $crate::__support::gather_parse!(@dispatch __set $(#[$imeta])* $vis static $name: ($($collection)*) ( $key ););
133    };
134
135    (@done ($($collection:tt)*) #[gather] $(#[$imeta:meta])* $vis:vis static $name:ident: ScatteredIterable < $ty:ty >; ) => {
136        $crate::__support::gather_parse!(@dispatch __iterable $(#[$imeta])* $vis static $name: ($($collection)*) ( $ty ););
137    };
138
139    (@done #[gather] $($rest:tt)* ) => {
140        compile_error!("Unknown collection type");
141    };
142
143    (@reorder (#[gather] $($item:tt)*) ($($rest:tt)*) $collection:tt) => {
144        $crate::__support::gather_parse!(@done $collection #[gather] $($rest)* $($item)*);
145    };
146    (@reorder (#[$top:meta] $($item:tt)*) ($($rest:tt)*) $collection:tt) => {
147        $crate::__support::gather_parse!(@reorder ($($item)*) (#[$top] $($rest)*) $collection);
148    };
149    (@reorder ($item:item;) $($rest:tt)*) => {
150        compile_error!("Missing #[gather] attribute.");
151    };
152    (@reorder $($rest:tt)*) => {
153        compile_error!("Missing #[gather] attribute.");
154    };
155
156    // Chomp through the path until we hit the collection's generics.
157    (@path ($($path_part:tt)*) ($(#$meta:tt)* $vis:vis static $name:ident) ($collection:ident < $($rest:tt)*)) => {
158        $crate::__support::gather_parse!(@reorder ($(#$meta)* $vis static $name: $collection < $($rest)*) () ($($path_part)* $collection));
159    };
160    (@path ($($path_part:tt)*) ($(#$meta:tt)* $vis:vis static $name:ident) ($next_path_part:ident $($rest:tt)*)) => {
161        $crate::__support::gather_parse!(@path ($($path_part)* $next_path_part) ($(#$meta)* $vis static $name) ($($rest)*));
162    };
163    (@path ($($path_part:tt)*) ($(#$meta:tt)* $vis:vis static $name:ident) (:: $($rest:tt)*)) => {
164        $crate::__support::gather_parse!(@path ($($path_part)* ::) ($(#$meta)* $vis static $name) ($($rest)*));
165    };
166    (@path ($($path_part:tt)*) ($(#$meta:tt)* $vis:vis static $name:ident) (:: $($rest:tt)*)) => {
167        compile_error!("Expected: #[gather] name: path::to::collection<generics>;");
168    };
169
170    // Handle a `crate_path = ...` override on the gather attribute: the
171    // proc-macro form already applied it to the support path reaching this
172    // macro, so strip it and continue.
173    (#[gather (crate_path = $($cp:tt)*)] $($rest:tt)*) => {
174        $crate::__support::gather_parse!(#[gather] $($rest)*);
175    };
176
177    ($(#$meta:tt)* $vis:vis static $name:ident: $collection:ident < $($rest:tt)* ) => {
178        $crate::__support::gather_parse!(@reorder ($(#$meta)* $vis static $name: $collection < $($rest)*) () ($collection));
179    };
180    ($(#$meta:tt)* $vis:vis static $name:ident: :: $($path:tt)* ) => {
181        $crate::__support::gather_parse!(@path (::) ($(#$meta)* $vis static $name) ($($path)*));
182    };
183    ($(#$meta:tt)* $vis:vis static $name:ident: $path_part:ident $($path:tt)* ) => {
184        $crate::__support::gather_parse!(@path ($path_part) ($(#$meta)* $vis static $name) ($($path)*));
185    };
186}
187
188#[doc(hidden)]
189#[allow(unused)]
190pub mod __support {
191    pub use crate::__gather_parse as gather_parse;
192    pub use crate::__scatter_parse as scatter_parse;
193
194    pub use ctor;
195    pub use link_section;
196    pub use linktime_proc_macro::combine;
197}
198
199/// Declarative `scatter!` / `gather!` entry points.
200///
201/// These are useful for cases where the scattered collections are used within
202/// your own macros.
203///
204/// The syntax is identical to the proc-macro form in both the `scatter` and
205/// `gather` cases, with the declarative macro wrapped around them.
206///
207/// ```rust
208/// use scattered_collect::declarative::{scatter, gather};
209/// use scattered_collect::slice::ScatteredSlice;
210///
211/// # fn main() {
212/// gather! {
213///   #[gather]
214///   static ITEMS: ScatteredSlice<u32>;
215/// }
216///
217/// scatter! {
218///   #[scatter(ITEMS)]
219///   const _: u32 = 1;
220/// }
221/// # }
222/// ```
223pub mod declarative {
224    /// Declarative form of the `#[gather]` macro.
225    ///
226    /// Useful for re-exporting these macros from other crates. Wrap this around
227    /// the proc-macro form:
228    ///
229    /// ```rust
230    /// # use scattered_collect::declarative::{scatter, gather};
231    /// # use scattered_collect::slice::ScatteredSlice;
232    ///
233    /// # fn main() {
234    /// gather! {
235    ///   #[gather]
236    ///   static ITEMS: ScatteredSlice<u32>;
237    /// }
238    ///
239    /// scatter! {
240    ///   #[scatter(ITEMS)]
241    ///   const _: u32 = 1;
242    /// }
243    /// # }
244    /// ```
245    #[doc(inline)]
246    pub use crate::__gather_brace as gather;
247    /// Declarative form of the `#[scatter]` macro.
248    ///
249    /// Useful for re-exporting these macros from other crates. Wrap this around
250    /// the proc-macro form:
251    ///
252    /// ```rust
253    /// # use scattered_collect::declarative::{scatter, gather};
254    /// # use scattered_collect::slice::ScatteredSlice;
255    ///
256    /// # fn main() {
257    /// gather! {
258    ///   #[gather]
259    ///   static ITEMS: ScatteredSlice<u32>;
260    /// }
261    ///
262    /// scatter! {
263    ///   #[scatter(ITEMS)]
264    ///   const _: u32 = 1;
265    /// }
266    /// # }
267    /// ```
268    #[doc(inline)]
269    pub use crate::__scatter_brace as scatter;
270}
271
272#[doc(inline)]
273pub use linktime_proc_macro::{gather, scatter};
274
275#[doc(hidden)]
276#[macro_export]
277macro_rules! __gather_brace {
278    ($($item:tt)*) => {
279        $crate::__support::gather_parse!($($item)*);
280    };
281}
282
283#[doc(hidden)]
284#[macro_export]
285macro_rules! __scatter_brace {
286    ($($item:tt)*) => {
287        $crate::__support::scatter_parse!($($item)*);
288    };
289}