generic_mutability/macros.rs
1/// This macro simplifies the implementation of generically mutable APIs, where the mutable and the shared code paths are (mostly) identical.
2/// It has the following syntax:
3///
4/// ```rust, ignore
5/// gen_mut!{ $M => {
6/// /* code */
7/// }}
8/// ```
9/// where `$M` is the name of the generic parameter you want to unwrap.
10/// The code inside has access to three macros, which allow it to emit different code depending on the value of `M`:
11///
12/// - `from_gen!($genref)` / `from_gen!()`
13///
14/// Calls `GenRef::gen_into_shared` and `GenRef::gen_into_mut` on the `$genref` passed as an argument.
15/// The type of return value is different in the shared vs the mutable case, so it is not possible to move the return value outside of the macro call (attempting to do so would run into a type checker error on trying to assign `&mut T` to `&T` or vice versa).
16/// The return value can be converted back into a `GenRef` using the `into_gen!` macro.
17/// If no arguments are passed, it returns a closure `Fn(GenRef<'_, M, T>) -> &T` / `Fn(GenRef<'_, M, T>) -> &mut T`.
18///
19/// - `into_mut!($reference)` / `into_mut!(&gen $place)` / `into_mut!()`
20///
21/// Calls `GenRef::gen_from_shared` and `GenRef::gen_from_mut` on the reference passed as an argument, and returns the resulting `GenRef`.
22/// The type of the input is different in the shared vs the mutable case, so it is not possible to call this with a reference that was not created via `from_gen!` or `switch_mut_shared!`.
23/// To allow accessing fields, you can use the `into_mut!(&gen $place)` syntax, which references the `$place` expression with the appropriate kind of reference.
24/// If no arguments are passed, it returns a closure `Fn(&T) -> GenRef<'_, M, T>` / `Fn(&mut T) -> GenRef<'_, M, T>`.
25///
26/// - `switch_shared_mut!($shared_expr, $mutable_expr)` / `switch_shared_mut!({ $shared_tts } { $mutable_tts })`
27///
28/// Expands to `shared_expr` in the shared case and `mutable_expr` in the mutable case.
29/// The `switch_shared_mut!({ $shared_tts } { $mutable_tts })` syntax allows you to expand to arbitrary token trees, not just expressions.
30/// This requires you to wrap them in brackets, which will not appear in the expansion.
31/// Also note that in this syntax there is no comma separating the two cases.
32
33#[macro_export]
34macro_rules! gen_mut {
35 ($m:ty => $code:expr) => {
36 match <$m as $crate::Mutability>::mutability() {
37 $crate::MutabilityEnum::Shared(proof) => {
38 macro_rules! into_gen {
39 () => {
40 |genref| $crate::GenRef::gen_from_shared(genref, proof)
41 };
42 (&gen $genref:expr) => {
43 $crate::GenRef::gen_from_shared(&$genref, proof)
44 };
45 ($genref:expr) => {
46 $crate::GenRef::gen_from_shared($genref, proof)
47 };
48 }
49 macro_rules! from_gen {
50 () => {
51 |genref| $crate::GenRef::gen_into_shared(genref, proof)
52 };
53 ($reference:expr) => {
54 $crate::GenRef::gen_into_shared($reference, proof)
55 };
56 }
57 #[allow(unused_macros)]
58 macro_rules! switch_shared_mut {
59 ($shared:tt $mutable:tt) => {
60 // For syntactic reasons, it is impossible to define a macro with a repeating capture group inside another macro.
61 // (The definition would be interpreted as a repeating expansion group of the outer macro.)
62 // So, processing is outsourced to a macro defined elsewhere.
63 $crate::__unwrap($shared);
64 };
65 ($shared:expr, $mutable:expr) => {
66 $shared
67 };
68 }
69 $code
70 }
71 $crate::MutabilityEnum::Mutable(proof) => {
72 macro_rules! into_gen {
73 () => {
74 |genref| $crate::GenRef::gen_from_mut(genref, proof)
75 };
76 (&gen $genref:expr) => {
77 $crate::GenRef::gen_from_mut(&mut $genref, proof)
78 };
79 ($genref:expr) => {
80 $crate::GenRef::gen_from_mut($genref, proof)
81 };
82 }
83 macro_rules! from_gen {
84 () => {
85 |genref| $crate::GenRef::gen_into_mut(genref, proof)
86 };
87 ($reference:expr) => {
88 $crate::GenRef::gen_into_mut($reference, proof)
89 };
90 }
91 #[allow(unused_macros)]
92 macro_rules! switch_shared_mut {
93 ($shared:tt $mutable:tt) => {
94 // For syntactic reasons, it is impossible to define a macro with a repeating capture group inside another macro.
95 // (The definition would be interpreted as a repeating expansion group of the outer macro.)
96 // So, processing is outsourced to a macro defined elsewhere.
97 $crate::__unwrap!($mutable)
98 };
99 ($shared:expr, $mutable:expr) => {
100 $mutable
101 };
102 }
103 $code
104 }
105 }
106 };
107}
108
109#[doc(hidden)]
110#[macro_export]
111macro_rules! __unwrap{
112 ( { $($items:tt)* } ) => {
113 $($items)*
114 }
115}
116
117/// Maps a `GenRef` over field access and indexing.
118///
119/// Returns a `GenRef` to the field. Accessing nested fields is supported.
120///
121/// The receiver (the expression returning `GenRef`) must be a single token (an identifier) or it must be wrapped in parentheses.
122///
123/// Examples:
124///
125/// ```rust, ignore
126/// field!(&gen genref.field)
127/// field!(&gen genref.field1.2.field3[4])
128/// field!(&gen (obtain_genref()).field)
129/// field!(&gen (container.genref).field)
130/// ```
131#[macro_export]
132macro_rules! field {
133 (&gen $genref:tt $($field:tt)+) => {
134 $crate::GenRef::map($genref, |r| & r $($field)+, |r| &mut r $($field)+)
135 };
136}