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 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
#![allow(rustdoc::broken_intra_doc_links)]
//! Procedural macros for `thiserror_ext`.
use expand::{DeriveCtorType, DeriveNewType};
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
mod expand;
mod thiserror;
/// Generates constructor functions for different variants of the error type.
///
/// The arguments of the constructor functions can be any types that implement
/// [`Into`] for the corresponding fields of the variant, enabling convenient
/// construction of the error type.
///
/// # Example
///
/// ```ignore
/// #[derive(Debug, thiserror::Error, thiserror_ext::Construct)]
/// enum Error {
/// #[error("unsupported feature: {0}")]
/// UnsupportedFeature { name: String },
///
/// #[error("internal error: {0}")]
/// #[construct(skip)] // to skip generating the constructor
/// InternalError(String),
/// }
///
/// // Any type that implements `Into<String>` is accepted as the argument.
/// let _: Error = Error::unsupported_feature("foo");
/// ```
///
/// # New type
///
/// If a new type is specified with `#[thiserror_ext(newtype(..))]`, the
/// constructors will be implemented on the new type instead.
///
/// See the documentation of [`thiserror_ext::Box`] or [`thiserror_ext::Arc`]
/// for more details.
///
/// [`thiserror_ext::Box`]: derive@Box
/// [`thiserror_ext::Arc`]: derive@Arc
#[proc_macro_derive(Construct, attributes(thiserror_ext, construct))]
pub fn derive_construct(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_ctor(&input, DeriveCtorType::Construct)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates extension traits for converting the external error type into the
/// the provided one, with extra context.
///
/// This can be helpful when the external error type does not provide enough
/// context for the application. `thiserror` does not allow specifying `#[from]`
/// on the error field if there're extra fields in the variant.
///
/// The extension trait is only generated when there's a field named `source`
/// or marked with `#[source]` but not `#[from]`. The rest of the fields (except
/// the backtrace) are treated as the context. Both single and multiple context
/// fields are supported.
///
/// # Example
///
/// ```ignore
/// #[derive(Debug, thiserror::Error, thiserror_ext::ContextInto)]
/// enum Error {
/// #[error("cannot parse int from `{from}`")]
/// ParseInt {
/// source: std::num::ParseIntError,
/// from: String,
/// },
///
/// #[error("cannot parse float from `{from}`")]
/// #[context_into(skip)] // to skip generating the extension
/// ParseFloat {
/// source: std::num::ParseIntError,
/// from: String,
/// },
/// }
///
/// // Specify the `from` as "foo" and convert it into `Error::ParseInt`.
/// let _: Error = "foo".parse::<i32>().unwrap_err().into_parse_int("foo");
///
/// // Can also be called on `Result<T, ExternalError>`
/// let _: Result<i32, Error> = "foo".parse().into_parse_int("foo");
///
/// // Call `into_*_with` with a closure to lazily evaluate the context.
/// let _: Result<i32, Error> = "foo".parse().into_parse_int_with(|| format!("{}", 1 + 1));
/// ```
///
/// # New type
///
/// If a new type is specified with `#[thiserror_ext(newtype(..))]`, the
/// extensions will convert the errors into the new type instead.
///
/// See the documentation of [`thiserror_ext::Box`] or [`thiserror_ext::Arc`]
/// for more details.
///
/// [`thiserror_ext::Box`]: derive@Box
/// [`thiserror_ext::Arc`]: derive@Arc
#[proc_macro_derive(ContextInto, attributes(thiserror_ext, context_into))]
pub fn derive_context_into(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_ctor(&input, DeriveCtorType::ContextInto)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates macros for different variants of the error type to construct
/// it or directly bail out.
///
/// # Inline formatting
///
/// It's common to put a message string in the error variant. With this macro,
/// one can directly format the message in the macro call, instead of calling
/// [`format!`].
///
/// To mark a field as the message to be formatted, name it `message` or mark
/// it with `#[message]`. The message field can be any type that implements
/// `From<String>`.
///
/// ## Example
///
/// ```ignore
/// #[derive(Debug, thiserror::Error, thiserror_ext::Macro)]
/// enum Error {
/// #[error("internal error: {msg}")]
/// Internal { #[message] msg: Box<str> },
/// }
///
/// // Equivalent to `Error::Internal { msg: format!(..).into() }`.
/// let _: Error = internal!("{} is a bad number", 42);
///
/// // Equivalent to `return Err(Error::Internal { msg: format!(..).into() }.into())`.
/// bail_internal!("{} is a bad number", 42);
/// ```
///
/// # Extra fields
///
/// If there're extra fields along with the message field, one can specify
/// the values of them with `field = value` syntax before the message. The
/// values can be any types that implement [`Into`] for the corresponding
/// fields.
///
/// Fields can be omitted, in which case [`Default::default()`] will be used.
///
/// ## Example
///
/// ```ignore
/// #[derive(Debug, thiserror::Error, thiserror_ext::Macro)]
/// #[error("not yet implemented: {message}")]
/// struct NotYetImplemented {
/// issue: Option<i32>,
/// pr: Option<i32>,
/// message: String,
/// }
///
/// let _: Error = not_yet_implemented!(issue = 42, pr = 88, "foo");
/// let _: Error = not_yet_implemented!(issue = 42, "foo"); // pr = None
/// let _: Error = not_yet_implemented!(pr = 88, "foo"); // issue = None
/// let _: Error = not_yet_implemented!("foo"); // issue = None, pr = None
/// ```
///
/// # Visibility
///
/// There's a different rule set for the visibility of the macros. The macros
/// generated by this proc-macro are marked with `#[macro_export]` only if the
/// visibility of the error type is `pub`, otherwise they're just re-exported
/// with the same visibility as the error type and only work in the same crate.
///
/// There're some extra configurations to help to better handle the visibility,
/// specified in `#[thiserror_ext(macro(..))]`:
///
/// - `vis = ..`: use a different visibility for the macro re-export.
/// - `mangle`: mangle the macro names so that they don't conflict with other
/// macros with the same name in the crate root.
/// - `path = "crate::.."`: the path to the current module. When specified,
/// types in the generated macros will use the qualified path like
/// `$crate::foo::bar::Error`, enabling the callers to use the macros without
/// importing the error type.
///
/// # New type
///
/// If a new type is specified with `#[thiserror_ext(newtype(..))]`, the macros
/// will generate the new type instead.
///
/// See the documentation of [`thiserror_ext::Box`] or [`thiserror_ext::Arc`]
/// for more details.
///
/// [`thiserror_ext::Box`]: derive@Box
/// [`thiserror_ext::Arc`]: derive@Arc
#[proc_macro_derive(Macro, attributes(thiserror_ext, message))]
pub fn derive_macro(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_macro(&input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates a new type that wraps the original error type in a [`struct@Box`].
///
/// Specify the name of the new type with `#[thiserror_ext(newtype(name = ..))]`.
///
/// # Reduce size
///
/// The most common motivation for using this macro is to reduce the size of
/// the original error type. As a sum-type, a [`Result`] is at least as large
/// as its largest variant. Large error type may hurt the performance of a
/// function call returning a [`Result`]. With this macro, the new type always
/// has the same size as a [`struct@Box`].
///
/// On the other hand, returning an error should be an exceptional case in most
/// cases. Therefore, even though boxing the error type may lead to extra
/// allocation, it's usually acceptable.
///
/// ## Example
///
/// ```ignore
/// #[derive(Debug, thiserror::Error, thiserror_ext::Box)]
/// #[thiserror_ext(newtype(name = Error))]
/// enum ErrorKind {
/// #[error("foo")]
/// Foo,
/// #[error("io")]
/// Io(#[from] std::io::Error),
/// }
///
/// // The size of `Error` is one pointer.
/// assert_eq!(std::mem::size_of::<Error>(), std::mem::size_of::<usize>());
///
/// // Convert to `Error`, from `ErrorKind` or other types that can be converted
/// // to `ErrorKind`.
/// let error: Error = ErrorKind::Foo.into();
/// let error: Error = io_error().into();
///
/// // Get the reference or the value of the inner error.
/// let _: &ErrorKind = error.inner();
/// let _: ErrorKind = error.into_inner();
/// ```
///
/// # Backtrace
///
/// Another use case is to capture backtrace when the error is created. Without
/// a new type, one has to manually add a [`Backtrace`] field to each variant
/// of the error type. The new type allows one to capture backtrace in a single
/// place.
///
/// Specify `#[thiserror_ext(newtype(.., backtrace))]` to enable capturing
/// backtrace. The extra backtrace is captured **only if** the original error
/// type does not [`provide`] one. Typically, this should be maintained by the
/// `#[backtrace]` attribute from `thiserror`.
///
/// ## Example
///
/// ```ignore
/// # use std::backtrace::Backtrace;
/// #[derive(Debug, thiserror::Error, thiserror_ext::Box)]
/// #[thiserror_ext(newtype(name = Error, backtrace))]
/// enum ErrorKind {
/// #[error("foo")]
/// Foo,
/// }
///
/// let error: Error = ErrorKind::Foo.into();
/// let backtrace: &Backtrace = std::error::request_ref(&error).unwrap();
/// ```
///
/// [`Backtrace`]: std::backtrace::Backtrace
/// [`provide`]: std::error::Error::provide
#[proc_macro_derive(Box, attributes(thiserror_ext))]
pub fn derive_box(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_new_type(&input, DeriveNewType::Box)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates a new type that wraps the original error type in an [`Arc`].
///
/// Specify the name of the new type with `#[thiserror_ext(newtype(name = ..))]`.
///
/// This is similar to [`thiserror_ext::Box`] but wraps the original error type
/// in an [`Arc`], so that it can always be cloned and shared across threads.
/// See [`thiserror_ext::Box`] for the explanation and examples.
///
/// [`Arc`]: std::sync::Arc
/// [`thiserror_ext::Box`]: derive@Box
#[proc_macro_derive(Arc, attributes(thiserror_ext))]
pub fn derive_arc(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_new_type(&input, DeriveNewType::Arc)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
/// Generates the [`Debug`] implementation that delegates to the [`Report`] of
/// an error.
///
/// Generally, the [`Debug`] representation of an error should not be used in
/// user-facing scenarios. However, if [`Result::unwrap`] or [`Result::expect`]
/// is called, or an error is used as [`Termination`], the standard library
/// will format the error with [`Debug`]. By delegating to [`Report`], we ensure
/// that the error is still formatted in a user-friendly way and the source
/// chain can be kept in these cases.
///
/// # Example
/// ```ignore
/// #[derive(thiserror::Error, thiserror_ext::ReportDebug)]
/// #[error("inner")]
/// struct Inner;
///
/// #[derive(thiserror::Error, thiserror_ext::ReportDebug)]
/// #[error("outer")]
/// struct Outer {
/// #[source]
/// inner: Inner,
/// }
///
/// let error = Outer { inner: Inner };
/// println!("{:?}", error);
/// ```
///
/// [`Report`]: thiserror_ext::Report
/// [`Termination`]: std::process::Termination
///
/// # New type
///
/// Since the new type delegates its [`Debug`] implementation to the original
/// error type, if the original error type derives [`ReportDebug`], the new type
/// will also behave the same.
#[proc_macro_derive(ReportDebug)]
pub fn derive_report_debug(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive_report_debug(&input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}