sort_by_derive/
lib.rs

1#![deny(clippy::all)]
2#![warn(clippy::pedantic)]
3#![warn(clippy::nursery)]
4#![warn(clippy::cargo)]
5#![allow(clippy::too_many_lines)]
6#![allow(clippy::similar_names)]
7#![allow(clippy::module_name_repetitions)]
8#![doc = include_str!("../README.md")]
9//! ## All together
10//!
11//! Imagine the following :
12//!
13//! ```rust
14//! # use sort_by_derive::SortBy;
15//! # use sort_by_derive::EnumAccessor;
16//! # use sort_by_derive::EnumSequence;
17//! # use std::cmp::Ordering;
18//! #
19//! #[derive(EnumSequence, EnumAccessor, SortBy, Debug)]
20//! #[accessor(global_time: usize)]
21//! #[accessor(channel: u8, except(CC))]
22//! #[accessor(pitch: u8, except(CC, Unsupported))]
23//! #[sort_by(global_time(), channel(), pitch(), enum_sequence())]
24//! enum Note {
25//! // ...
26//! #    NoteOn {
27//! #        global_time: usize,
28//! #        pitch: u8,
29//! #        channel: u8
30//! #    },
31//! #    NoteOff {
32//! #        global_time: usize,
33//! #        pitch: u8,
34//! #        channel: u8
35//! #    },
36//! #    CC {
37//! #        global_time: usize
38//! #    },
39//! #    Unsupported {
40//! #        global_time: usize,
41//! #        raw_data: Vec<u8>,
42//! #        channel: u8
43//! #    }
44//! }
45//!
46//! assert_eq!(
47//!     Note::NoteOn {
48//!         global_time: 0,
49//!         pitch: 0,
50//!         channel: 0,
51//!     }.cmp(&Note::NoteOn {
52//!         global_time: 0,
53//!         pitch: 0,
54//!         channel: 0,
55//!     }),
56//!     Ordering::Equal
57//! );
58//! assert_eq!(
59//!     Note::NoteOn {
60//!         global_time: 0,
61//!         pitch: 2,
62//!         channel: 2,
63//!     }.cmp(&Note::NoteOff {
64//!         global_time: 2,
65//!         pitch: 0,
66//!         channel: 0,
67//!     }),
68//!     Ordering::Less
69//! );
70//! assert_eq!(
71//!     Note::NoteOn {
72//!         global_time: 0,
73//!         pitch: 2,
74//!         channel: 0,
75//!     }.cmp(&Note::NoteOff {
76//!         global_time: 0,
77//!         pitch: 0,
78//!         channel: 2,
79//!     }),
80//!     Ordering::Less
81//! );
82//! assert_eq!(
83//!     Note::NoteOn {
84//!         global_time: 0,
85//!         pitch: 0,
86//!         channel: 0,
87//!     }.cmp(&Note::NoteOff {
88//!         global_time: 0,
89//!         pitch: 0,
90//!         channel: 2,
91//!     }),
92//!     Ordering::Less
93//! );
94//! assert_eq!(
95//!     Note::NoteOn {
96//!         global_time: 0,
97//!         pitch: 0,
98//!         channel: 0,
99//!     }.cmp(&Note::NoteOff {
100//!         global_time: 0,
101//!         pitch: 0,
102//!         channel: 0,
103//!     }),
104//!     Ordering::Less
105//! );
106//! ```
107//!
108//! Now I have a `Note` enum that will sort by `global_time`, `channel`, `pitch`, and lastly by variant order ( `enum_sequence` ). Note that `None` is always less than `Some`.
109//!
110//! Conversely, separate structs such as `NoteOn` may derive from `SortBy` in order to ignore some fields ( ex: `velocity` may be a `f32`, so we can't directly derive `Ord` ).
111use syn::{parse_macro_input, DeriveInput};
112
113mod enum_sequence;
114mod enum_variant_accessor;
115mod sort_by;
116
117/// Fields that should be used for sorting are marked with the attribute `#[sort_by]`.
118/// Other fields will be ignored.
119///
120/// ```rust
121/// # use std::cmp::Ordering;
122/// use sort_by_derive::SortBy;
123///
124/// #[derive(SortBy)]
125/// struct Something {
126///     #[sort_by]
127///     a: u16,
128///     b: u16
129/// }
130///
131/// assert_eq!(Something{a: 2, b: 0}.cmp(&Something{a: 1, b: 1}), Ordering::Greater); // a is compared
132/// assert_eq!(Something{a: 1, b: 0}.cmp(&Something{a: 1, b: 1}), Ordering::Equal); // b is ignored
133/// ```
134/// You can use it the same way with tuple structs. Tuple members can be referenced by their index in the struct-level
135/// `#[sort_by(...)]` attribute.
136///
137/// ```rust
138/// # use std::cmp::Ordering;
139/// # use sort_by_derive::SortBy;
140/// #
141/// #[derive(SortBy)]
142/// #[sort_by(0)]
143/// struct Something(
144///     u16,
145///     #[sort_by]
146///     u32,
147///     f32
148/// );
149///
150/// assert_eq!(Something(1, 0, 1.0).cmp(&Something(1, 0, 2.0)), Ordering::Equal); // Compares only specified fields
151/// assert_eq!(Something(2, 0, 1.0).cmp(&Something(1, 0, 2.0)), Ordering::Greater); // Compares only specified fields
152/// ```
153///
154///
155/// Alternatively, or in combination with, a struct-level or enum-level `#[sort_by(method1(),method2(),attr1,nested.attr)]` can be declared.
156/// This top-level declaration takes precedence, fields comparison will be considered if top-level comparisons are all `eq`.
157/// The top-level `sort_by` attribute takes a list of attributes or method calls; items will be prepended with `self.`.
158///
159/// ```rust
160/// # use std::cmp::Ordering;
161/// # use sort_by_derive::SortBy;
162/// #
163/// #[derive(SortBy)]
164/// #[sort_by(product())]
165/// struct Something {
166///     #[sort_by]
167///     a: u16,
168///     b: u16,
169/// }
170///
171/// impl Something {
172///     fn product(&self) -> u16 {
173///         self.a * self.b
174///     }
175/// }
176///
177/// assert_eq!(Something{a: 1, b: 1}.cmp(&Something{a: 1, b: 2}), Ordering::Less); // method comparison precedes member comparison
178/// assert_eq!(Something{a: 2, b: 0}.cmp(&Something{a: 1, b: 0}), Ordering::Greater); // method comparison is equal (0 = 0) so fall back to member comparison
179/// ```
180///
181/// ## Limitation
182///
183/// - struct-level `sort_by` attribute always comes before field-level attributes lexicographically.
184#[proc_macro_derive(SortBy, attributes(sort_by))]
185pub fn sort_by_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
186    let ast = parse_macro_input!(input as DeriveInput);
187    sort_by::impl_sort_by_derive(ast).into()
188}
189
190/// This derive macro is similar to [enum_dispatch](https://crates.io/crates/enum_dispatch).
191/// `enum_dispatch` requires structs to implement a common trait, which can be useful if a common set of functions applies to all variants.
192/// `EnumAccessor` takes the opposite approach: common fields and methods are declared at enum level, and you can have variants that don't have a given field or method.
193/// This may be more practical if there is a large amount of variants and your only concern is accessing fields, because individual structs just hold data.
194/// This is typical for events - they represent a state change and are generally consumed as a whole, individual structs have no code of their own.
195///
196/// #### Field accessor
197///
198/// After adding `derive(EnumAccessor)` to the enum, fields are declared as `accessor(field: type)` attributes:
199///
200/// This will derive the accessor methods `fn name(&self) -> &type;` and`fn name_mut(&mut self) -> &mut type;`, and return a reference to the field of the same name on any variant.
201///
202/// ```rust
203/// use sort_by_derive::EnumAccessor;
204///
205/// #[derive(EnumAccessor)]
206/// #[accessor(a: u16)]
207/// #[accessor(b: u16)]
208/// enum E {
209///     Variant1{a: u16, b: u16},
210///     Variant2{a: u16, b: u16, c: u32},
211/// }
212///
213/// let v1 = E::Variant1{a: 1, b: 1};
214/// let mut v2 = E::Variant2{a: 1, b: 1, c: 2};
215///
216/// // Accessor methods are generated for the specified members
217/// assert_eq!(*v1.a(), 1);
218/// assert_eq!(*v2.b(), 1);
219///
220/// // Mutable accessors are also generated
221/// *v2.a_mut() = 2;
222/// assert_eq!(*v2.a(), 2);
223/// ```
224///
225/// So you can take any `E`, all variants will have `a`, `a_mut`, `b`, `b_mut`
226///
227/// ```rust
228/// # use sort_by_derive::EnumAccessor;
229/// #
230/// #[derive(EnumAccessor)]
231/// #[accessor(a: u16)]
232/// #[accessor(b: u16)]
233/// enum E {
234///     Variant1{a: u16, b: u16},
235///     Variant2{a: u16, b: u16, c: u32},
236/// }
237///
238/// fn do_something(e: &mut E) -> u16 {
239///     let field_value = *e.a(); // take the value of that field, whatever variant it is
240///     *e.a_mut() = 42; // use the accessor method returning a &mut to the field
241///     field_value
242/// }
243///
244/// let mut v1 = E::Variant1{a: 11, b: 0};
245/// assert_eq!(do_something(&mut v1), 11);
246/// assert_eq!(*v1.a(), 42);
247///
248/// let mut v2 = E::Variant2{a: 11, b: 0, c: 32};
249/// assert_eq!(do_something(&mut v2), 11);
250/// assert_eq!(*v2.a(), 42);
251/// ```
252///
253/// Use `except` or `only` if not all variants have a given field:
254///
255/// ```rust
256/// # use sort_by_derive::EnumAccessor;
257/// #
258/// #[derive(EnumAccessor)]
259/// #[accessor(a: u16, only(Variant1, Variant2))]
260/// #[accessor(c: u32, except(Variant1, Variant3))]
261/// enum E {
262///     Variant1 { a: u16, b: u16 },
263///     Variant2 { a: u16, b: u16, c: u32 },
264///     Variant3 { d: u64 },
265///     Variant4 { c: u32 },
266/// }
267///
268/// assert_eq!(E::Variant1 { a: 1, b: 2 }.a(), Some(&1));
269/// assert_eq!(E::Variant2 { a: 1, b: 2, c: 0 }.a(), Some(&1));
270/// assert_eq!(E::Variant3 { d: 0 }.a(), None);
271/// assert_eq!(E::Variant2 { a: 0, b: 0, c: 2 }.c(), Some(&2));
272/// assert_eq!(E::Variant1 { a: 0, b: 0 }.c(), None);
273/// ```
274/// This derives the same accessor methods, but the return type will be `Option<&type>` and `Option<&mut type>`.
275/// The provided comma-separated list of variants in `except` will return `None`.
276/// The provided comma-separated list of variants in `only` must have the given field and will return `Some`.
277///
278/// Methods without arguments ( i.e. only `&self` are also supported ).
279/// It takes the form: `#[accessor(method_name(): type)]`.
280/// If `type` is a `&mut`, the generated method will take `&mut self` instead of `&self`.
281/// This can be useful for accessing mutable derived methods of nested enums.
282///
283/// To avoid name clashes, accessors can be given an alias by using `as`:
284///
285/// ```rust
286/// # use sort_by_derive::EnumAccessor;
287/// #
288/// #[derive(EnumAccessor)]
289/// #[accessor(a as a_attr: u16, except(Variant3))]
290/// enum E {
291///     Variant1 { a: u16 },
292///     Variant2 { a: u16, c: u32 },
293///     Variant3 { b: u32 },
294/// }
295///
296/// impl E {
297///     fn a(&self) -> bool {
298///         // Unrelated work
299///         #        true
300///     }
301/// }
302///
303/// assert_eq!(E::Variant1 { a: 1 }.a(), true);
304/// assert_eq!(E::Variant1 { a: 1 }.a_attr(), Some(&1));
305/// assert_eq!(E::Variant2 { a: 1, c: 0 }.a_attr(), Some(&1));
306/// assert_eq!(E::Variant3 { b: 0 }.a_attr(), None);
307/// ```
308///
309///
310/// #### Example
311///
312/// Say we have a series of midi events, they are very similar but with slight variations - they always have some timing information but they may not always have a pitch or channel.
313///
314/// Using `#[accessor(global_time: usize)]`, a `global_time(&self)` method is derived, along with a `global_time_mut(&mut self)`, so without any boilerplate you can access the timing.
315///
316/// By declaring `#[accessor(channel: u8, except(CC))]`, `channel(&self)` and `channel_mut(&mut self)` are derived, but they return `Some` for `NoteOn` and `NoteOff`, and `None` for `CC` and `Unsupported`.
317///
318/// ```rust
319/// # use sort_by_derive::EnumAccessor;
320/// #
321/// #[derive(EnumAccessor)]
322/// #[accessor(global_time: usize)]
323/// #[accessor(channel: u8, except(CC))]
324/// #[accessor(pitch: u8, except(CC, Unsupported))]
325/// enum Note {
326///     NoteOn {
327///         global_time: usize,
328///         pitch: u8,
329///         channel: u8
330///     },
331///     NoteOff {
332///         global_time: usize,
333///         pitch: u8,
334///         channel: u8
335///     },
336///     CC {
337///         global_time: usize
338///     },
339///     Unsupported {
340///         global_time: usize,
341///         raw_data: Vec<u8>,
342///         channel: u8
343///     }
344/// }
345///
346/// assert!(
347///     [
348///         *Note::NoteOn {
349///             global_time: 42,
350///             // ...
351///             #            pitch: 5,
352///             #            channel: 3,
353///         }.global_time(),
354///         *Note::NoteOff {
355///             global_time: 42,
356///             // ...
357///             #            pitch: 2,
358///             #            channel: 1,
359///         }.global_time(),
360///         *Note::CC {
361///             global_time: 42,
362///         }.global_time(),
363///         *Note::Unsupported {
364///             global_time: 42,
365///             // ...
366///             #            raw_data: vec![1, 2, 4, 8],
367///             #            channel: 4,
368///         }.global_time()
369///     ].into_iter().all(|t| t == 42)
370/// );
371///
372/// assert_eq!(
373///     Note::NoteOn {
374///         // ...
375///         #        global_time: 42,
376///         pitch: 2,
377///         // ...
378///         #        channel: 0,
379///     }.pitch(),
380///     Some(&2)
381/// );
382///
383/// assert_eq!(
384///     Note::CC {
385///         global_time: 42,
386///     }.pitch(),
387///     None
388/// );
389/// ```
390///
391/// #### Method accessor
392///
393/// The general form is `#[accessor(method():type)]`.
394///
395/// As for field access, declaring an exception will make the actual return type an `Option<type>`.
396///
397/// Named fields is supported, it will consider that the named field is of type `Fn() -> type`, and call it.
398///
399/// An intricate example:
400///
401/// ```rust
402/// # use sort_by_derive::EnumAccessor;
403/// # use std::sync::Arc;
404/// # use std::sync::atomic::{AtomicU8, Ordering};
405/// #
406/// struct A {
407///     f1: u8,
408///     f2: u8
409/// }
410///
411/// impl A {
412///     fn sum(&self) -> u8 {
413///         // ...
414///         #        self.f1 + self.f2
415///     }
416///     fn set(&mut self) -> &mut u8 {
417///         // ...
418///         #        &mut self.f1
419///     }
420/// }
421///
422/// struct B {
423///     values: Vec<u8>
424/// }
425///
426/// impl B {
427///     fn sum(&self) -> u8 {
428///         // ...
429///         #        self.values.iter().sum()
430///     }
431/// }
432///
433/// #[derive(EnumAccessor)]
434/// #[accessor(sum():u8)]
435/// #[accessor(set(): &mut u8, except(B,C))]
436/// enum E<Get: Fn() -> u8> {
437///     A(A),
438///     B(B),
439///     C{sum: Get}
440/// }
441///
442/// let factor = Arc::new(AtomicU8::new(1));
443///
444/// let [mut a, b, c] = [
445/// E::A(A { f1: 10, f2: 22 }),
446/// E::B(B { values: vec![9, 4, 3, 2] }),
447/// {
448/// let factor = factor.clone();
449/// E::C {
450/// sum: move || 21 * factor.load(Ordering::Relaxed),
451/// }
452/// }];
453///
454/// assert_eq!(32, a.sum()); // sum() is available without matching against E::A, E::B or E::C
455/// if let Some(value) = a.set() { // set() is only available for E::A and returns a &mut u8, so we get a Option<&mut u8>
456/// *value = 0;
457/// }
458/// assert_eq!(22, a.sum());
459/// assert_eq!(18, b.sum());
460/// assert_eq!(21, c.sum());
461/// factor.store(2, Ordering::Relaxed);
462/// assert_eq!(42, c.sum());
463/// ```
464///
465/// ## Limitation
466///
467/// - On unnamed variants, `EnumAccessor` only considers the first parameter.
468#[proc_macro_derive(EnumAccessor, attributes(accessor))]
469pub fn enum_variant_accessor_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
470    let ast = parse_macro_input!(input as DeriveInput);
471    enum_variant_accessor::impl_enum_accessor(ast).into()
472}
473
474/// Simply derive `EnumSequence`, and you get `enum_sequence(&self)` which returns a `usize`, starting from `0` and incrementing for each variant.
475///
476/// When using enums of enums, creating an accessor to the inner enum's sequence may create a method name ambiguity. To mitigate this, a custom accessor name can be chosen by using `as`, for instance `#[accessor(enum_sequence() as inner_sequence: usize)]`
477///
478/// **Note**: this will create an extension trait `{TypeName}EnumSequence` ( i.e. the type `T` will get a new trait `TEnumSequence` ). This trait will have the same visibility as the type. When using this type from another module, make sure to bring the trait in scope with `use {TypeName}EnumSequence`.
479///
480/// #### Example
481///
482/// ```rust
483/// use sort_by_derive::EnumSequence;
484///
485/// #[derive(EnumSequence)]
486/// enum ABC {
487///     A(u8),
488///     B(String),
489///     C { f: String, g: usize }
490/// }
491///
492/// assert_eq!(ABC::B("hi!".into()).enum_sequence(), 1);
493/// ```
494///
495#[proc_macro_derive(EnumSequence)]
496pub fn enum_sequence_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
497    let ast = parse_macro_input!(input as DeriveInput);
498    enum_sequence::impl_enum_sequence(ast).into()
499}