Skip to main content

suzunari_error_macro_impl/
lib.rs

1//! Procedural macros for `suzunari-error`.
2//!
3//! Provides 3 proc-macros:
4//!
5//! - [`#[suzunari_error]`](suzunari_error) — The main entry point. Processes
6//!   `#[suzu(...)]` attributes, resolves/injects location fields, and appends
7//!   `#[derive(Debug, Snafu, StackError)]`.
8//! - [`#[derive(StackError)]`](derive_stack_error) — Generates `StackError` impl
9//!   and `From<T> for BoxedStackError` (when `alloc` enabled). Finds location field
10//!   via `#[stack(location)]` marker or `Location` type.
11//! - [`#[report]`](report) — Transforms `fn() -> Result<(), E>` into
12//!   `fn() -> StackReport<E>` for formatted error output on failure (`std` only).
13
14mod attribute;
15mod derive;
16mod helper;
17mod report;
18mod suzu_attr;
19
20use crate::attribute::suzunari_error_impl;
21use crate::derive::stack_error_impl;
22use crate::report::report_impl;
23use proc_macro::TokenStream;
24
25/// Derives the [`StackError`] trait for a struct or enum.
26///
27/// Requires a `Location` field in every struct/variant (added automatically
28/// by `#[suzunari_error]`). The location field is resolved by:
29/// 1. `#[stack(location)]` marker — highest priority, supports any field name
30/// 2. Single field of type `Location` — automatic fallback
31/// 3. Error if neither is found
32///
33/// When using `#[suzunari_error]`, `#[suzu(location)]` on a field becomes
34/// `#[stack(location)]` + `#[snafu(implicit)]`.
35///
36/// Also generates `From<T> for BoxedStackError` when the `alloc` feature is enabled.
37#[proc_macro_derive(StackError, attributes(stack))]
38pub fn derive_stack_error(input: TokenStream) -> TokenStream {
39    stack_error_impl(input.into())
40        .unwrap_or_else(|err| err.to_compile_error())
41        .into()
42}
43
44/// The main entry point for defining error types.
45///
46/// Processes `#[suzu(...)]` attributes (suzunari extensions + snafu passthrough),
47/// resolves/injects location fields, and appends
48/// `#[derive(Debug, Snafu, StackError)]`.
49///
50/// # `#[suzu(...)]` attributes
51///
52/// `#[suzu(...)]` is a superset of `#[snafu(...)]`. All snafu keywords are
53/// passed through as-is. Additionally:
54///
55/// - **`from`** (field-level): Wraps the field type in `DisplayError<T>` and
56///   generates a `source(from(...))` conversion that automatically preserves the
57///   `Error::source()` chain when the wrapped type implements `Error`.
58/// - **`location`** (field-level): Marks a field as the location field. Converts
59///   to `#[stack(location)]` + `#[snafu(implicit)]`. Allows custom field names
60///   instead of the default `location`. Requires a `Location` type.
61#[proc_macro_attribute]
62pub fn suzunari_error(attr: TokenStream, item: TokenStream) -> TokenStream {
63    let attr2: proc_macro2::TokenStream = attr.into();
64    if !attr2.is_empty() {
65        use syn::spanned::Spanned;
66        return syn::Error::new(
67            attr2.span(),
68            "#[suzunari_error] does not accept arguments; use #[suzu(...)] on fields instead",
69        )
70        .to_compile_error()
71        .into();
72    }
73    suzunari_error_impl(item.into())
74        .unwrap_or_else(|err| err.to_compile_error())
75        .into()
76}
77
78/// Transforms `fn() -> Result<(), E>` into `fn() -> StackReport<E>`.
79///
80/// Primarily designed for `fn main()` where `StackReport`'s `Termination` impl
81/// formats error chains on failure. Can also be applied to other functions to
82/// convert `Result<(), E>` to `StackReport<E>` (e.g., for testing).
83///
84/// # Usage
85///
86/// Use the qualified path `#[suzunari_error::report]` (not `#[report]` alone):
87///
88/// ```rust,ignore
89/// use suzunari_error::*;
90///
91/// #[suzunari_error::report]
92/// fn main() -> Result<(), AppError> {
93///     run()?;
94///     Ok(())
95/// }
96/// ```
97///
98/// # Limitations
99///
100/// - Does not support generics, `where` clauses, `async fn`, `const fn`,
101///   `unsafe fn`, or `extern fn`.
102/// - Return type must be written literally as `Result<(), E>` (type aliases
103///   are not resolved).
104/// - Function parameters with complex patterns (e.g., `(a, b): (u32, u32)`)
105///   are forwarded as-is to the generated closure, which may not compile
106///   depending on the pattern form.
107#[proc_macro_attribute]
108pub fn report(attr: TokenStream, item: TokenStream) -> TokenStream {
109    report_impl(attr.into(), item.into())
110        .unwrap_or_else(|err| err.to_compile_error())
111        .into()
112}