1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2022 Robert Oleynik

use proc_macro::TokenStream;
use syn::DeriveInput;

mod error;

/// Derive macro to simplify error type implementation. Implements [`std::fmt::Display`],
/// [`std::error::Error`] for the derived Type.
///
/// # Creating new Error Type
///
/// **Note:** The created type must be an enum.
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error {
///     #[error("Error 1")]
///     Variant1,
///     #[error("Error: {}")]
///     Variant2(i32),
///     // ...
/// }
///
/// assert_eq!(format!("{}", Error::Variant1), "Error 1");
/// assert_eq!(format!("{}", Error::Variant2(42)), "Error: 42");
/// ```
///
/// This will create an enum with name `Error` which implements the [`std::fmt::Display`] and
/// [`std::error::Error`] trait. The output of `format!` depends on the message specified for the
/// variants (For more details see: [Changing The Error Message](#changing-the-error-message)).
///
/// # Changing the Error Message
///
/// As seen above the error message can be changed per enum variant by using the `error` attribute.
/// The default error message of unnamed enum variants (e.g. `Error::Variant2`) with one field is
/// `"{}"`. For all other variants the message must be specified.
///
/// ## Error Message of Unit Variants
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error {
///     // ...
///     #[error("<message>")]
///     UnitVariant
/// }
/// ```
///
/// The usage of `"{}"` inside of `<message>` has no effect on the result of [`std::fmt::Display`].
///
/// ## Error Message of Unnamed Variants
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error {
///     // ...
///     #[error("<message with {}>")]
///     UnnamedVariant(i32)
/// }
/// ```
///
/// The use of `"{}"` is required by the error message. To display brackets use `{{ }}`. For more
/// semantics see [`std::format_args`].
///
/// If you use more than one field in unnamed variants you have to use multiple `{}` for the
/// fields:
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error {
///     // ...
///     #[error("error message with multiple fields: (field 1: {}, field 2: {})")]
///     UnnamedVariant(i32, f32)
/// }
/// ```
///
/// **Note:** Every field of the variant **must** be used.
///
/// ## Error Message of Named Variants
///
/// Named enum variants are not supported yet. Use unnamed enum variants instead (See [Error
/// Message Of Unnamed Variants](#error-message-of-unnamed-variants)).
///
/// # Using `Errors` with `try!` or `?`
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error {
///     // ...
///     #[error(from)]
///     Io(std::io::Error)
/// }
///
/// fn read_file<P: AsRef<std::path::Path>>(path: P) -> Result<(), Error> {
///     let _content = std::fs::read_to_string(path)?;
///     // ...
///     Ok(())
/// }
/// ```
///
/// Passing `from` to the attribute `error` will result in the implementation of `From` for the
/// field of the variant.
///
/// Some limits if you use `from`:
///
/// - `from` can only be used with 1-field unnamed enum variants.
/// - `from` can not be used on multiple enum variants with same field type. (You have to select
///   one of them)
/// - `from` can not be used with templated variants and other enum variants at the same time. (You
///   have to select one of them)
///
/// # Using `Errors` with Type Parameters/Templates
///
/// ```rust
/// use error_utils_derive::Errors;
/// // or "use error_utils::Errors" if you use the error_utils crate.
///
/// #[derive(Debug, Errors)]
/// enum Error<T: std::fmt::Debug + std::fmt::Display> {
///     #[error("{}", from)]
///     E(T),
///     #[error("Some other error")]
///     Other
/// }
/// ```
#[proc_macro_derive(Errors, attributes(error))]
pub fn derive_errors(item: TokenStream) -> TokenStream {
	let item = syn::parse_macro_input!(item as DeriveInput);
	error::Collection::from(item).generate().into()
}