fack_macro/
lib.rs

1use proc_macro::TokenStream;
2
3use fack_codegen::{Target, expand::Expand};
4
5/// Derive the `Error` trait for a struct.
6///
7/// The `#[derive(Error)]` macro provides a declarative way to implement
8/// both the [`Error`] and [`Display`] traits for your custom error types.
9/// It is controlled by `#[error(...)]` *type-level attributes* that specify
10/// how the error should behave.
11///
12/// # General form
13///
14/// ```ignore
15/// #[derive(Error, Debug)]
16/// #[error("a formatted message {arg}", arg)]
17/// // #[error(source(...))]
18/// // #[error(transparent(...))]
19/// // #[error(from)]
20/// struct MyError { /* fields */ }
21/// ```
22///
23/// The attribute syntax follows the form:
24///
25/// ```text
26/// #[error(<parameter>)]
27/// ```
28///
29/// Multiple `#[error(...)]` attributes can be applied to the same type,
30/// and are classified according to their *parameter kind*.
31///
32///
33/// # Attribute kinds
34///
35/// The following attribute kinds are supported:
36///
37/// ## `#[error("...")]` - format string
38///
39/// Provides a [`Display`] implementation for the error type, using
40/// Rust formatting syntax. Additional expressions may be listed as
41/// comma-separated arguments.
42///
43/// ```
44/// #[derive(Error, Debug)]
45/// #[error("failed to read file: {path}", path)]
46/// struct ReadError {
47///     path: String,
48/// }
49/// ```
50///
51/// Expands to:
52///
53/// ```ignore
54/// impl std::fmt::Display for ReadError {
55///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56///         write!(f, "failed to read file: {}", self.path)
57///     }
58/// }
59/// ```
60///
61/// ## `#[error(source(<field>))]` - source field
62///
63/// Declares which field represents the underlying cause of the error.
64/// This field will be returned from `Error::source`.
65///
66/// ```
67/// #[derive(Error, Debug)]
68/// #[error("network request failed")]
69/// #[error(source(io))]
70/// struct NetworkError {
71///     io: std::io::Error,
72/// }
73/// ```
74///
75/// Produces:
76///
77/// ```ignore
78/// impl std::error::Error for NetworkError {
79///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
80///         Some(&self.io)
81///     }
82/// }
83/// ```
84///
85/// ## `#[error(transparent(<field>))]` - transparent wrapper
86///
87/// Makes the error type a transparent wrapper around one of its fields.
88/// Both [`Display`] and [`Error`] are forwarded directly to that field.
89///
90/// ```
91/// #[derive(Error, Debug)]
92/// #[error(transparent)]
93/// struct Wrapper(std::io::Error);
94/// ```
95///
96/// This makes `Wrapper` behave exactly like its inner error for
97/// formatting and chaining purposes.
98///
99///
100/// ## `#[error(from)]` conversion
101///
102/// Requests that a `From<T>` implementation be generated for a specified
103/// field type, allowing automatic conversion into the error type.
104///
105/// ```
106/// #[derive(Error, Debug)]
107/// #[error("parse failed")]
108/// #[error(from)]
109/// struct ParseError {
110///     inner: std::num::ParseIntError,
111/// }
112/// ```
113///
114/// Allows:
115///
116/// ```ignore
117/// let err: ParseError = "abc".parse::<u32>().unwrap_err().into();
118/// ```
119///
120///
121/// # Enums
122///
123/// Enums may use the same attribute kinds at the *variant* level.
124/// Each variant can provide its own formatting, source, or transparency.
125///
126/// ```
127/// #[derive(Error, Debug)]
128/// enum MyError {
129///     #[error("invalid input: {_0}")]
130///     InvalidInput(String),
131///
132///     #[error(transparent(inner))]
133///     Io {
134///         inner: std::io::Error,
135///     },
136///
137///     #[error("other error")]
138///     Other,
139/// }
140/// ```
141///
142///
143/// # Inline options
144///
145/// Inline behavior for generated methods can be controlled using:
146///
147/// ```
148/// #[derive(Error, Debug)]
149/// #[error(inline(always))]
150/// #[error("{msg}")]
151/// struct FastError {
152///     msg: &'static str,
153/// }
154/// ```
155///
156/// Valid values are:
157/// - `neutral` (default) → `#[inline]`
158/// - `always` → `#[inline(always)]`
159/// - `never` → `#[inline(never)]`
160///
161/// When omitted, no explicit `#[inline(...)]` attribute is emitted.
162///
163///
164/// # Root import
165///
166/// All generated code defaults to using `::core`. This can be overridden:
167///
168/// ```
169/// #[derive(Error, Debug)]
170/// #[error(import(::std))]
171/// struct StdError {
172///     msg: &'static str,
173/// }
174/// ```
175///
176/// This allows compatibility with `std::error::Error` where desired.
177///
178/// [`Error`]: error
179/// [`Display`]: core::fmt::Display
180#[proc_macro_derive(Error, attributes(error))]
181pub fn error(input: TokenStream) -> TokenStream {
182    let ref input = syn::parse_macro_input!(input as syn::DeriveInput);
183
184    match Target::input(input).map(Expand::target) {
185        Ok(Ok(target_stream)) => TokenStream::from(target_stream),
186        Err(target_error) | Ok(Err(target_error)) => target_error.to_compile_error().into(),
187    }
188}