whichever_macros/lib.rs
1//! Procedural macros of `whichever`.
2//!
3//! `#[dispatchable]` on a trait re-emits the trait and exports its carrier
4//! macro, a hidden `macro_rules!` that carries the trait's tokens; the macro
5//! is re-exported under the trait's own name, so importing the trait imports
6//! the macro. `#[dispatch(Trait)]` on an enum invokes that macro, which
7//! forwards the trait tokens, the enum tokens and the options into the
8//! implementation macro, `implement!`, which writes `impl Trait for Enum`
9//! with one `match` per method. No state is shared between expansions, so
10//! the trait and the enum may live in different crates.
11use proc_macro::TokenStream;
12use proc_macro2::Span;
13use quote::quote;
14use syn::{parse_macro_input, Error, Item, ItemTrait};
15
16use crate::block::{expand_inherent, expand_partial};
17use crate::dispatch::{expand_block, expand_subject};
18use crate::dispatchable::expand_dispatchable;
19use crate::implement::expand_implement;
20use crate::options::DispatchableOptions;
21use crate::protocol::{BlockInput, ImplementInput};
22use crate::subject::Subject;
23
24/// Returns the span a generated token carries: located at `span`, a token
25/// the user wrote, so that an error rustc finds in the generated code lands
26/// there, and resolved at the expansion's own site, so that lints treat the
27/// token as the expansion's and not as the user's.
28pub(crate) fn at(span: Span) -> Span {
29 Span::mixed_site().located_at(span)
30}
31
32/// Folds `errors` into the first, so that all of them are reported at once.
33pub(crate) fn combined(errors: Vec<Error>) -> Option<Error> {
34 let mut errors = errors.into_iter();
35 let mut first = errors.next()?;
36 for error in errors {
37 first.combine(error);
38 }
39 Some(first)
40}
41
42mod agreement;
43mod block;
44mod conversions;
45mod dispatch;
46mod dispatchable;
47mod forwarding;
48mod implement;
49#[cfg(test)]
50mod limits;
51mod naming;
52mod options;
53mod protocol;
54mod route;
55mod selection;
56mod shape;
57#[cfg(test)]
58mod spans;
59mod subject;
60mod substitution;
61
62/// Makes a trait available to `#[dispatch]`, from this crate or any other.
63///
64/// The trait is emitted unchanged, except that `#[dispatch(..)]` attributes
65/// on its methods are consumed. The only method option is `mismatch`, which
66/// gives the result of a method taking `Self` arguments when the arguments
67/// hold a different variant than `self`:
68///
69/// ```rust,ignore
70/// #[whichever::dispatchable]
71/// pub trait Shape {
72/// fn area(&self) -> u32;
73/// #[dispatch(mismatch = false)]
74/// fn congruent(&self, other: &Self) -> bool;
75/// }
76/// ```
77///
78/// Options:
79/// - `remote = path` declares a trait of another crate: the item is a
80/// restatement of that trait's signatures, nothing is emitted for it but
81/// the carrier macro, and `#[dispatch]` of the declaration implements the
82/// trait at `path`. Only the methods restated are forwarded.
83/// - `name = ident` names the carrier macro after `ident` instead of the
84/// trait's text and position, for a trait that one `macro_rules!` emits
85/// several times with identical tokens.
86///
87/// ```rust,ignore
88/// #[whichever::dispatchable(remote = std::io::Write)]
89/// trait Write {
90/// fn write(&mut self, buf: &[u8]) -> std::io::Result<usize>;
91/// fn flush(&mut self) -> std::io::Result<()>;
92/// fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()>;
93/// }
94/// ```
95///
96/// The trait's signature types are later written into the dispatching
97/// enum's module, so they must be nameable there; `crate::` paths are
98/// rewritten to this crate, `super::` and `self::` paths are not and must
99/// not be used.
100#[proc_macro_attribute]
101pub fn dispatchable(attr: TokenStream, item: TokenStream) -> TokenStream {
102 let options = parse_macro_input!(attr as DispatchableOptions);
103 let item_trait = parse_macro_input!(item as ItemTrait);
104 let site = proc_macro::Span::call_site();
105 expand_dispatchable(item_trait, options, site.line(), site.column())
106 .unwrap_or_else(|error| error.to_compile_error())
107 .into()
108}
109
110/// Implements a `#[dispatchable]` trait for an enum by forwarding every
111/// method to whichever variant the value holds, or forwards the methods of
112/// an inherent `impl` block of such an enum.
113///
114/// On an enum, each attribute names one trait, with its generic arguments
115/// when it has any, followed by options; several attributes dispatch several
116/// traits, and a bare `#[dispatch]` only registers the enum. Every variant
117/// must hold exactly one unnamed field whose type implements the trait. The
118/// enum also gets a `From<Payload>` impl for every payload type that appears
119/// in exactly one variant.
120///
121/// ```rust,ignore
122/// #[whichever::dispatch(Shape)]
123/// #[whichever::dispatch(Encoder<Json>, Error = EncodeError)]
124/// pub enum AnyShape {
125/// Circle(Circle),
126/// #[dispatch(deref)]
127/// Square(Box<Square>),
128/// }
129/// ```
130///
131/// Options, after the trait path:
132/// - `Name = Type`: the associated type `Name` of the enum's impl, instead
133/// of the first variant's. Methods returning `Self::Name` are converted
134/// with `Into`, methods returning `Result<_, Self::Name>` with
135/// `map_err(Into::into)`.
136/// - `mismatch(method = expr, ..)`: the result of `method` when its `Self`
137/// arguments hold a different variant than `self`.
138/// - `from = false`: no `From<Payload>` impls.
139/// - `try_from = true`: a `TryFrom<Enum> for Payload` impl per payload type,
140/// returning the enum unchanged when it holds another variant.
141///
142/// Three options go on a variant. `#[dispatch(deref)]` calls the trait on what
143/// the payload dereferences to, for `Box<T>`, `Rc<T>`, `Arc<T>`, `&T` and
144/// `&mut T` payloads; such a variant cannot take part in a method that
145/// returns `Self`. `#[dispatch(from = false)]` leaves the variant out of the
146/// conversions, and `#[dispatch(try_from = true)]` adds the `TryFrom` impl
147/// for that payload alone.
148///
149/// On a struct, the trait is forwarded to its one field, or to the field
150/// marked `#[dispatch]`, with `#[dispatch(deref)]` for a field behind a
151/// pointer. A struct gets no conversions, needs no `mismatch`, and forwards
152/// a return-position `impl Trait` as it is, since its `match` has one arm.
153///
154/// On an inherent `impl` block of a dispatch enum, in the same crate, every
155/// method carrying `#[dispatch(..)]` is forwarded to the named variants'
156/// own method of the same name, and the body is what the other variants
157/// return. `*` names every variant; the method then has no fallback and is
158/// written without a body:
159///
160/// ```rust,ignore
161/// #[whichever::dispatch]
162/// impl AnyShape {
163/// /// Only circles have a radius.
164/// #[dispatch(Circle, with = Some)]
165/// pub fn radius(&self) -> Option<f64> {
166/// None
167/// }
168///
169/// #[dispatch(*)]
170/// pub fn perimeter(&self) -> f64;
171/// }
172/// ```
173///
174/// Method options: `with = path` wraps each forwarded result in a call to
175/// `path`, and `call = name` forwards to a payload method of another name.
176/// Methods without the attribute are left as they are.
177///
178/// On a trait impl block of a dispatch enum, in the same crate, the block
179/// holds the items the enum implements itself, a generic associated type
180/// among them, and the rest of the trait is forwarded around them. The enum
181/// then does not name that trait in its own attribute; the trait's options
182/// go on the block's attribute:
183///
184/// ```rust,ignore
185/// #[whichever::dispatch]
186/// pub enum Handler {
187/// Text(TextHandler),
188/// Map(MapHandler),
189/// }
190///
191/// #[whichever::dispatch]
192/// impl HandlerTrait for Handler {
193/// fn from_handler(handler: Handler) -> Option<Self> {
194/// Some(handler)
195/// }
196/// }
197/// ```
198#[proc_macro_attribute]
199pub fn dispatch(attr: TokenStream, item: TokenStream) -> TokenStream {
200 let site = proc_macro::Span::call_site();
201 let (line, column) = (site.line(), site.column());
202 let item = parse_macro_input!(item as Item);
203 let expanded = match item {
204 Item::Enum(item_enum) => {
205 expand_subject(attr.into(), Subject::Enum(item_enum), line, column)
206 }
207 Item::Struct(item_struct) => {
208 expand_subject(attr.into(), Subject::Struct(item_struct), line, column)
209 }
210 Item::Impl(item_impl) => expand_block(attr.into(), item_impl),
211 other => {
212 let error = Error::new_spanned(
213 &other,
214 "`#[dispatch]` goes on an enum or a struct, or on an impl block of one",
215 )
216 .to_compile_error();
217 quote! { #other #error }
218 }
219 };
220 expanded.into()
221}
222
223/// Writes the trait impl; called by the macro that `#[dispatchable]` exports.
224#[doc(hidden)]
225#[proc_macro]
226pub fn implement(input: TokenStream) -> TokenStream {
227 let input = parse_macro_input!(input as ImplementInput);
228 expand_implement(input)
229 .unwrap_or_else(|error| error.to_compile_error())
230 .into()
231}
232
233/// Rewrites an impl block of a dispatch enum or struct; called by the macro
234/// that `#[dispatch]` defines for the item.
235#[doc(hidden)]
236#[proc_macro]
237pub fn implement_block(input: TokenStream) -> TokenStream {
238 let input = parse_macro_input!(input as BlockInput);
239 let expanded = match input.partial {
240 Some(spec) => expand_partial(
241 input.rejected,
242 &input.facade,
243 &input.dispatched,
244 input.remote.as_ref(),
245 input.subject,
246 spec,
247 input.item_impl,
248 ),
249 None => expand_inherent(
250 input.rejected,
251 input.remote.as_ref(),
252 input.subject,
253 input.item_impl,
254 ),
255 };
256 expanded
257 .unwrap_or_else(|error| error.to_compile_error())
258 .into()
259}