whichever/lib.rs
1//! Implements a trait for an enum by forwarding to whichever variant it holds.
2//!
3//! `#[dispatchable]` goes on the trait, in the crate that owns it.
4//! `#[dispatch(Trait)]` goes on an enum whose variants each hold one value
5//! implementing the trait, in any crate. The enum then implements the trait
6//! with one `match` per method, each arm a fully qualified call into the
7//! variant's payload, and gets a `From<Payload>` impl per variant.
8//!
9//! ```
10//! // In the crate that owns the trait.
11//! #[whichever::dispatchable]
12//! pub trait Shape {
13//! fn area(&self) -> u32;
14//! fn name(&self) -> &'static str {
15//! "shape"
16//! }
17//! }
18//!
19//! pub struct Square(pub u32);
20//! pub struct Rect(pub u32, pub u32);
21//!
22//! impl Shape for Square {
23//! fn area(&self) -> u32 {
24//! self.0 * self.0
25//! }
26//! fn name(&self) -> &'static str {
27//! "square"
28//! }
29//! }
30//!
31//! impl Shape for Rect {
32//! fn area(&self) -> u32 {
33//! self.0 * self.1
34//! }
35//! }
36//!
37//! // In any crate that owns an enum over implementors.
38//! #[whichever::dispatch(Shape)]
39//! pub enum AnyShape {
40//! Square(Square),
41//! Rect(Rect),
42//! }
43//!
44//! fn main() {
45//! let square: AnyShape = Square(3).into();
46//! let rect: AnyShape = Rect(2, 5).into();
47//! assert_eq!((square.area(), square.name()), (9, "square"));
48//! assert_eq!((rect.area(), rect.name()), (10, "shape"));
49//! }
50//! ```
51//!
52//! # What is generated
53//!
54//! For the enum above, `#[dispatch(Shape)]` expands to the impl a careful
55//! hand would write, plus `#[inline]` and the conversions:
56//!
57//! ```rust,ignore
58//! impl Shape for AnyShape {
59//! #[inline]
60//! fn area(&self) -> u32 {
61//! match self {
62//! AnyShape::Square(inner) => <Square as Shape>::area(inner),
63//! AnyShape::Rect(inner) => <Rect as Shape>::area(inner),
64//! }
65//! }
66//! #[inline]
67//! fn name(&self) -> &'static str {
68//! match self {
69//! AnyShape::Square(inner) => <Square as Shape>::name(inner),
70//! AnyShape::Rect(inner) => <Rect as Shape>::name(inner),
71//! }
72//! }
73//! }
74//!
75//! impl From<Square> for AnyShape { /* .. */ }
76//! impl From<Rect> for AnyShape { /* .. */ }
77//! ```
78//!
79//! Default methods are always forwarded, so a variant's override is never
80//! hidden by the trait's default at the enum level. The calls are fully
81//! qualified, so an inherent method on a payload with the same name as a
82//! trait method does not take over. There is no trait object, no closure
83//! and no conversion in the arms unless an option asks for one.
84//!
85//! # Options
86//!
87//! All options follow the trait path in `#[dispatch(..)]`:
88//!
89//! - `Name = Type` fixes the associated type `Name` instead of taking it
90//! from the first variant. A method returning `Self::Name` is then
91//! converted with [`Into`], and one returning `Result<_, Self::Name>` with
92//! `map_err(Into::into)`, so variants whose own type converts into the
93//! named one can share the enum. See [associated types](#associated-types).
94//! - `mismatch(method = expr, ..)` gives the result of `method` when its
95//! `Self` arguments hold a different variant than `self`. The trait author
96//! can decide this once instead, with `#[dispatch(mismatch = expr)]` on
97//! the method. See [`Self` arguments](#self-arguments).
98//! - `from = false` suppresses the `From<Payload>` impls.
99//! - `try_from = true` adds `TryFrom<Enum> for Payload` for every payload
100//! type, with the enum itself as the error, so a mismatch hands the value
101//! back.
102//!
103//! One option goes on a variant: `#[dispatch(deref)]` calls the trait on
104//! what the payload dereferences to, so `Box<T>`, `Rc<T>`, `Arc<T>`, `&T`
105//! and `&mut T` payloads dispatch like a plain `T`. See
106//! [dereferenced payloads](#dereferenced-payloads).
107//!
108//! Several traits are dispatched by repeating the attribute, one trait per
109//! attribute; a supertrait is just one more attribute:
110//!
111//! ```rust,ignore
112//! #[whichever::dispatch(Shape)]
113//! #[whichever::dispatch(Drawable)]
114//! pub enum AnyShape { /* .. */ }
115//! ```
116//!
117//! # Semantics
118//!
119//! | Trait item | Enum impl |
120//! | --- | --- |
121//! | `type X;` | `type X = <FirstVariant as Trait>::X;`, or the `X = Type` option. Every other variant is checked to agree; a disagreement is reported on that variant. |
122//! | `const N: T;` | `const N: T = <FirstVariant as Trait>::N;` |
123//! | method with a receiver | A `match` over the variants calling `<Payload as Trait>::method(payload, args..)`. |
124//! | default body | Ignored: the method is forwarded like any other. |
125//! | `async fn` | Each arm is awaited. |
126//! | `unsafe fn` | Each call is wrapped in `unsafe { }`. |
127//! | argument `Self`, `&Self`, `&mut Self` | Matched on the same variant as `self`; different variants give the `mismatch` expression. |
128//! | return `Self` | Wrapped back into the same variant; `Option<Self>` and `Result<Self, E>` are mapped. |
129//! | return `Self::X` with `X` overridden | Converted with `Into`; `Result<_, Self::X>` with `map_err(Into::into)`. |
130//! | method without a receiver | Left to the trait's default body; an error when there is none. |
131//! | `#[cfg]` on a method or variant | Carried onto the generated method or arm. |
132//! | `#[inline]` | Added to every generated method unless the trait method carries its own. |
133//!
134//! # Generics
135//!
136//! A generic trait is dispatched at a concrete instantiation, named on the
137//! enum: `#[dispatch(Encoder<Json>)]`. Type, lifetime and const parameters
138//! are substituted in every signature, and a projection such as `F::Value`
139//! becomes `<Json as Format>::Value` when `F` has exactly one bound. The
140//! enum itself may be generic; its parameters, bounds and `where` clause
141//! carry into the impl.
142//!
143//! # `Self` arguments
144//!
145//! A method such as `fn congruent(&self, other: &Self) -> bool` compares two
146//! values of the enum. The generated method matches both on the same variant
147//! and calls the payload's method; for two different variants it returns the
148//! expression given by `#[dispatch(mismatch = false)]` on the trait method,
149//! or by `mismatch(congruent = false)` on the enum. Without one the expansion
150//! fails with an error naming the method. Only `Self`, `&Self` and
151//! `&mut Self` are accepted; a `Self` nested in another type
152//! (`Option<&Self>`) is an error.
153//!
154//! # Associated types
155//!
156//! Without an override the enum takes each associated type from its first
157//! variant, and a hidden check asserts that every other variant uses the
158//! same type, with the error on the variant that differs. With
159//! `Error = StorageError` the enum's type is `StorageError`, and each
160//! variant's own error type only has to implement `Into<StorageError>`:
161//!
162//! ```
163//! #[whichever::dispatchable]
164//! pub trait Storage {
165//! type Error;
166//! fn load(&self, key: &str) -> Result<u32, Self::Error>;
167//! }
168//!
169//! #[derive(Debug, PartialEq)]
170//! pub struct StorageError(pub String);
171//!
172//! pub struct Memory;
173//! pub struct Disk;
174//!
175//! impl Storage for Memory {
176//! type Error = StorageError;
177//! fn load(&self, key: &str) -> Result<u32, StorageError> {
178//! Ok(key.len() as u32)
179//! }
180//! }
181//!
182//! impl Storage for Disk {
183//! type Error = &'static str;
184//! fn load(&self, key: &str) -> Result<u32, &'static str> {
185//! Err("offline")
186//! }
187//! }
188//!
189//! impl From<&'static str> for StorageError {
190//! fn from(message: &'static str) -> Self {
191//! StorageError(message.to_owned())
192//! }
193//! }
194//!
195//! #[whichever::dispatch(Storage, Error = StorageError)]
196//! pub enum AnyStorage {
197//! Memory(Memory),
198//! Disk(Disk),
199//! }
200//!
201//! fn main() {
202//! let disk: AnyStorage = Disk.into();
203//! assert_eq!(disk.load("answer"), Err(StorageError("offline".to_owned())));
204//! }
205//! ```
206//!
207//! # Dereferenced payloads
208//!
209//! A variant marked `#[dispatch(deref)]` holds its value behind a pointer or
210//! a reference. The trait is called on `<Payload as Deref>::Target`, with
211//! the receiver and any `Self` argument dereferenced to match: `&**inner`
212//! for `&self`, `&mut **inner` for `&mut self` (the payload must implement
213//! `DerefMut`), `*inner` for `self` (a `Box` moves out, a reference cannot).
214//! A method returning `Self` cannot wrap its result back into such a
215//! payload, so it is rejected for a `deref` variant.
216//!
217//! ```
218//! use std::rc::Rc;
219//!
220//! #[whichever::dispatchable]
221//! pub trait Area {
222//! fn area(&self) -> u32;
223//! }
224//!
225//! pub struct Square(pub u32);
226//!
227//! impl Area for Square {
228//! fn area(&self) -> u32 {
229//! self.0 * self.0
230//! }
231//! }
232//!
233//! #[whichever::dispatch(Area)]
234//! pub enum Shape<'a> {
235//! #[dispatch(deref)]
236//! Boxed(Box<Square>),
237//! #[dispatch(deref)]
238//! Shared(Rc<Square>),
239//! #[dispatch(deref)]
240//! Borrowed(&'a Square),
241//! Plain(Square),
242//! }
243//!
244//! fn main() {
245//! let square = Square(3);
246//! assert_eq!(Shape::Boxed(Box::new(Square(2))).area(), 4);
247//! assert_eq!(Shape::Borrowed(&square).area(), 9);
248//! }
249//! ```
250//!
251//! # Inherent methods
252//!
253//! An enum often needs methods of its own that only some variants can
254//! answer. `#[dispatch]` on an inherent `impl` block of a dispatch enum
255//! forwards each method marked `#[dispatch(..)]` to the named variants'
256//! method of the same name, and the body is what every other variant
257//! returns. `*` names every variant, and the method then has no body:
258//!
259//! ```
260//! #[whichever::dispatchable]
261//! pub trait Shape {
262//! fn area(&self) -> u32;
263//! }
264//!
265//! pub struct Circle(pub u32);
266//! pub struct Square(pub u32);
267//! # impl Shape for Circle { fn area(&self) -> u32 { 3 * self.0 * self.0 } }
268//! # impl Shape for Square { fn area(&self) -> u32 { self.0 * self.0 } }
269//!
270//! impl Circle {
271//! pub fn radius(&self) -> u32 {
272//! self.0
273//! }
274//! pub fn describe(&self) -> String {
275//! format!("circle {}", self.0)
276//! }
277//! }
278//!
279//! impl Square {
280//! pub fn describe(&self) -> String {
281//! format!("square {}", self.0)
282//! }
283//! }
284//!
285//! #[whichever::dispatch(Shape)]
286//! pub enum AnyShape {
287//! Circle(Circle),
288//! Square(Square),
289//! }
290//!
291//! #[whichever::dispatch]
292//! impl AnyShape {
293//! /// Only circles have a radius.
294//! #[dispatch(Circle, with = Some)]
295//! pub fn radius(&self) -> Option<u32> {
296//! None
297//! }
298//!
299//! #[dispatch(*)]
300//! pub fn describe(&self) -> String;
301//! }
302//!
303//! fn main() {
304//! let square: AnyShape = Square(2).into();
305//! assert_eq!(square.radius(), None);
306//! assert_eq!(square.describe(), "square 2");
307//! }
308//! ```
309//!
310//! `with = path` wraps each forwarded result in a call to `path` (`Some`
311//! above), `call = name` forwards to a payload method of another name, and
312//! methods without the attribute are left alone. The enum keeps its
313//! definition in a crate-local macro for this, so the `impl` block has to be
314//! in the enum's crate, which inherent impls are anyway. A bare `#[dispatch]`
315//! on an enum registers it without dispatching a trait.
316//!
317//! # Across crates
318//!
319//! `#[dispatchable]` emits the trait unchanged plus a hidden exported macro
320//! carrying the trait's tokens, re-exported under the trait's own name
321//! (macros live in their own namespace, so `use shapes::Shape;` imports
322//! both). `#[dispatch(Shape)]` invokes that macro, which hands the trait
323//! and the enum to a hidden proc macro that writes the impl. Nothing is
324//! cached between expansions, so the two attributes work across crates, in
325//! any order, and under rust-analyzer. Both crates depend on `whichever`; a
326//! renamed dependency is fine.
327//!
328//! The types named in the trait's signatures are written into the enum's
329//! module, so they must be in scope there, exactly as for a hand-written
330//! impl. Paths starting with `crate::` are rewritten to the trait's crate;
331//! `super::` and `self::` paths cannot be and must not appear in a
332//! dispatchable trait's signatures.
333//!
334//! # Requirements and limits
335//!
336//! - Every variant holds exactly one unnamed field, `Name(Payload)`, and
337//! every payload implements the trait. A payload type used by two variants
338//! gets no `From` impl.
339//! - A method without a receiver needs a default body; the enum uses it.
340//! - Return-position `impl Trait` cannot be dispatched by a `match`: every
341//! variant would return a different opaque type.
342//! - Generic associated types and `self: Type` receivers are not supported.
343//! - Associated consts are taken from the first variant without a check.
344//!
345//! "cannot find macro `Shape` in this scope" at the enum means the trait is
346//! not marked `#[dispatchable]`, or the trait (which carries the macro) is
347//! not imported where the enum is.
348//!
349//! The crate is `no_std`: the generated code names only `core` items. The
350//! minimum supported Rust version is 1.88.
351#![no_std]
352#![forbid(unsafe_code)]
353#![deny(missing_docs)]
354
355pub use whichever_macros::{dispatch, dispatchable};
356
357#[doc(hidden)]
358pub mod __private {
359 pub use whichever_macros::{implement, implement_inherent};
360}