error_utils_derive/
lib.rs

1// SPDX-License-Identifier: MIT
2//
3// Copyright (c) 2022 Robert Oleynik
4
5use proc_macro::TokenStream;
6use syn::DeriveInput;
7
8mod error;
9
10/// Derive macro to simplify error type implementation. Implements [`std::fmt::Display`],
11/// [`std::error::Error`] for the derived Type.
12///
13/// # Creating new Error Type
14///
15/// **Note:** The created type must be an enum.
16///
17/// ```rust
18/// use error_utils_derive::Errors;
19/// // or "use error_utils::Errors" if you use the error_utils crate.
20///
21/// #[derive(Debug, Errors)]
22/// enum Error {
23///     #[error("Error 1")]
24///     Variant1,
25///     #[error("Error: {}")]
26///     Variant2(i32),
27///     // ...
28/// }
29///
30/// assert_eq!(format!("{}", Error::Variant1), "Error 1");
31/// assert_eq!(format!("{}", Error::Variant2(42)), "Error: 42");
32/// ```
33///
34/// This will create an enum with name `Error` which implements the [`std::fmt::Display`] and
35/// [`std::error::Error`] trait. The output of `format!` depends on the message specified for the
36/// variants (For more details see: [Changing The Error Message](#changing-the-error-message)).
37///
38/// # Changing the Error Message
39///
40/// As seen above the error message can be changed per enum variant by using the `error` attribute.
41/// The default error message of unnamed enum variants (e.g. `Error::Variant2`) with one field is
42/// `"{}"`. For all other variants the message must be specified.
43///
44/// ## Error Message of Unit Variants
45///
46/// ```rust
47/// use error_utils_derive::Errors;
48/// // or "use error_utils::Errors" if you use the error_utils crate.
49///
50/// #[derive(Debug, Errors)]
51/// enum Error {
52///     // ...
53///     #[error("<message>")]
54///     UnitVariant
55/// }
56/// ```
57///
58/// The usage of `"{}"` inside of `<message>` has no effect on the result of [`std::fmt::Display`].
59///
60/// ## Error Message of Unnamed Variants
61///
62/// ```rust
63/// use error_utils_derive::Errors;
64/// // or "use error_utils::Errors" if you use the error_utils crate.
65///
66/// #[derive(Debug, Errors)]
67/// enum Error {
68///     // ...
69///     #[error("<message with {}>")]
70///     UnnamedVariant(i32)
71/// }
72/// ```
73///
74/// The use of `"{}"` is required by the error message. To display brackets use `{{ }}`. For more
75/// semantics see [`std::format_args`].
76///
77/// If you use more than one field in unnamed variants you have to use multiple `{}` for the
78/// fields:
79///
80/// ```rust
81/// use error_utils_derive::Errors;
82/// // or "use error_utils::Errors" if you use the error_utils crate.
83///
84/// #[derive(Debug, Errors)]
85/// enum Error {
86///     // ...
87///     #[error("error message with multiple fields: (field 1: {}, field 2: {})")]
88///     UnnamedVariant(i32, f32)
89/// }
90/// ```
91///
92/// **Note:** Every field of the variant **must** be used.
93///
94/// ## Error Message of Named Variants
95///
96/// Named enum variants are not supported yet. Use unnamed enum variants instead (See [Error
97/// Message Of Unnamed Variants](#error-message-of-unnamed-variants)).
98///
99/// # Using `Errors` with `try!` or `?`
100///
101/// ```rust
102/// use error_utils_derive::Errors;
103/// // or "use error_utils::Errors" if you use the error_utils crate.
104///
105/// #[derive(Debug, Errors)]
106/// enum Error {
107///     // ...
108///     #[error(from)]
109///     Io(std::io::Error)
110/// }
111///
112/// fn read_file<P: AsRef<std::path::Path>>(path: P) -> Result<(), Error> {
113///     let _content = std::fs::read_to_string(path)?;
114///     // ...
115///     Ok(())
116/// }
117/// ```
118///
119/// Passing `from` to the attribute `error` will result in the implementation of `From` for the
120/// field of the variant.
121///
122/// Some limits if you use `from`:
123///
124/// - `from` can only be used with 1-field unnamed enum variants.
125/// - `from` can not be used on multiple enum variants with same field type. (You have to select
126///   one of them)
127/// - `from` can not be used with templated variants and other enum variants at the same time. (You
128///   have to select one of them)
129///
130/// # Using `Errors` with Type Parameters/Templates
131///
132/// ```rust
133/// use error_utils_derive::Errors;
134/// // or "use error_utils::Errors" if you use the error_utils crate.
135///
136/// #[derive(Debug, Errors)]
137/// enum Error<T: std::fmt::Debug + std::fmt::Display> {
138///     #[error("{}", from)]
139///     E(T),
140///     #[error("Some other error")]
141///     Other
142/// }
143/// ```
144#[proc_macro_derive(Errors, attributes(error))]
145pub fn derive_errors(item: TokenStream) -> TokenStream {
146	let item = syn::parse_macro_input!(item as DeriveInput);
147	error::Collection::from(item).generate().into()
148}