1 //! This library provides a convenient derive macro for the standard library's
2 //! [`core::fmt::Display`] trait.
3 //!
4 //! [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
5 //!
6 //! ```toml
7 //! [dependencies]
8 //! displaydoc = "0.2"
9 //! ```
10 //!
11 //! *Compiler support: requires rustc 1.56+*
12 //!
13 //! <br>
14 //!
15 //! ## Example
16 //!
17 //! *Demonstration alongside the [`Error`][std::error::Error] derive macro from [`thiserror`](https://docs.rs/thiserror/1.0.25/thiserror/index.html),
18 //! to propagate source locations from [`io::Error`][std::io::Error] with the `#[source]` attribute:*
19 //! ```rust
20 //! use std::io;
21 //! use displaydoc::Display;
22 //! use thiserror::Error;
23 //!
24 //! #[derive(Display, Error, Debug)]
25 //! pub enum DataStoreError {
26 //! /// data store disconnected
27 //! Disconnect(#[source] io::Error),
28 //! /// the data for key `{0}` is not available
29 //! Redaction(String),
30 //! /// invalid header (expected {expected:?}, found {found:?})
31 //! InvalidHeader {
32 //! expected: String,
33 //! found: String,
34 //! },
35 //! /// unknown data store error
36 //! Unknown,
37 //! }
38 //!
39 //! let error = DataStoreError::Redaction("CLASSIFIED CONTENT".to_string());
40 //! assert!("the data for key `CLASSIFIED CONTENT` is not available" == &format!("{}", error));
41 //! ```
42 //! *Note that although [`io::Error`][std::io::Error] implements `Display`, we do not add it to the
43 //! generated message for `DataStoreError::Disconnect`, since it is already made available via
44 //! `#[source]`. See further context on avoiding duplication in error reports at the rust blog
45 //! [here](https://github.com/yaahc/blog.rust-lang.org/blob/master/posts/inside-rust/2021-05-15-What-the-error-handling-project-group-is-working-towards.md#duplicate-information-issue).*
46 //!
47 //! <br>
48 //!
49 //! ## Details
50 //!
51 //! - A `fmt::Display` impl is generated for your enum if you provide
52 //! a docstring comment on each variant as shown above in the example. The
53 //! `Display` derive macro supports a shorthand for interpolating fields from
54 //! the error:
55 //! - `/// {var}` ⟶ `write!("{}", self.var)`
56 //! - `/// {0}` ⟶ `write!("{}", self.0)`
57 //! - `/// {var:?}` ⟶ `write!("{:?}", self.var)`
58 //! - `/// {0:?}` ⟶ `write!("{:?}", self.0)`
59 //! - This also works with structs and [generic types][crate::Display#generic-type-parameters]:
60 //! ```rust
61 //! # use displaydoc::Display;
62 //! /// oh no, an error: {0}
63 //! #[derive(Display)]
64 //! pub struct Error<E>(pub E);
65 //!
66 //! let error: Error<&str> = Error("muahaha i am an error");
67 //! assert!("oh no, an error: muahaha i am an error" == &format!("{}", error));
68 //! ```
69 //!
70 //! - Two optional attributes can be added to your types next to the derive:
71 //!
72 //! - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc
73 //! comment attributes (or `///` lines) after the first. Multi-line
74 //! comments using `///` are otherwise treated as an error, so use this
75 //! attribute or consider switching to block doc comments (`/** */`).
76 //!
77 //! - `#[prefix_enum_doc_attributes]` combines the doc comment message on
78 //! your enum itself with the messages for each variant, in the format
79 //! “enum: variant”. When added to an enum, the doc comment on the enum
80 //! becomes mandatory. When added to any other type, it has no effect.
81 //!
82 //! - In case you want to have an independent doc comment, the
83 //! `#[displaydoc("...")` atrribute may be used on the variant or struct to
84 //! override it.
85 //!
86 //! <br>
87 //!
88 //! ## FAQ
89 //!
90 //! 1. **Is this crate `no_std` compatible?**
91 //! * Yes! This crate implements the [`core::fmt::Display`] trait, not the [`std::fmt::Display`] trait, so it should work in `std` and `no_std` environments. Just add `default-features = false`.
92 //!
93 //! 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?**
94 //! * Yuuup. This crate uses @dtolnay's [autoref specialization technique](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md) to add a special trait for types to get the display impl. It then specializes for `Path` and `PathBuf`, and when either of these types are found, it calls `self.display()` to get a `std::path::Display<'_>` type which can be used with the `Display` format specifier!
95 #![doc(html_root_url = "https://docs.rs/displaydoc/0.2.3")]
96 #![cfg_attr(docsrs, feature(doc_cfg))]
97 #![warn(
98 rust_2018_idioms,
99 unreachable_pub,
100 bad_style,
101 dead_code,
102 improper_ctypes,
103 non_shorthand_field_patterns,
104 no_mangle_generic_items,
105 overflowing_literals,
106 path_statements,
107 patterns_in_fns_without_body,
108 unconditional_recursion,
109 unused,
110 unused_allocation,
111 unused_comparisons,
112 unused_parens,
113 while_true
114 )]
115 #![allow(clippy::try_err)]
116
117 #[allow(unused_extern_crates)]
118 extern crate proc_macro;
119
120 mod attr;
121 mod expand;
122 mod fmt;
123
124 use proc_macro::TokenStream;
125 use syn::{parse_macro_input, DeriveInput};
126
127 /// [Custom `#[derive(...)]` macro](https://doc.rust-lang.org/edition-guide/rust-2018/macros/custom-derive.html)
128 /// for implementing [`fmt::Display`][core::fmt::Display] via doc comment attributes.
129 ///
130 /// ### Generic Type Parameters
131 ///
132 /// Type parameters to an enum or struct using this macro should *not* need to
133 /// have an explicit `Display` constraint at the struct or enum definition
134 /// site. A `Display` implementation for the `derive`d struct or enum is
135 /// generated assuming each type parameter implements `Display`, but that should
136 /// be possible without adding the constraint to the struct definition itself:
137 /// ```rust
138 /// use displaydoc::Display;
139 ///
140 /// /// oh no, an error: {0}
141 /// #[derive(Display)]
142 /// pub struct Error<E>(pub E);
143 ///
144 /// // No need to require `E: Display`, since `displaydoc::Display` adds that implicitly.
145 /// fn generate_error<E>(e: E) -> Error<E> { Error(e) }
146 ///
147 /// assert!("oh no, an error: muahaha" == &format!("{}", generate_error("muahaha")));
148 /// ```
149 ///
150 /// ### Using [`Debug`][core::fmt::Debug] Implementations with Type Parameters
151 /// However, if a type parameter must instead be constrained with the
152 /// [`Debug`][core::fmt::Debug] trait so that some field may be printed with
153 /// `{:?}`, that constraint must currently still also be specified redundantly
154 /// at the struct or enum definition site. If a struct or enum field is being
155 /// formatted with `{:?}` via [`displaydoc`][crate], and a generic type
156 /// parameter must implement `Debug` to do that, then that struct or enum
157 /// definition will need to propagate the `Debug` constraint to every type
158 /// parameter it's instantiated with:
159 /// ```rust
160 /// use core::fmt::Debug;
161 /// use displaydoc::Display;
162 ///
163 /// /// oh no, an error: {0:?}
164 /// #[derive(Display)]
165 /// pub struct Error<E: Debug>(pub E);
166 ///
167 /// // `E: Debug` now has to propagate to callers.
168 /// fn generate_error<E: Debug>(e: E) -> Error<E> { Error(e) }
169 ///
170 /// assert!("oh no, an error: \"cool\"" == &format!("{}", generate_error("cool")));
171 ///
172 /// // Try this with a struct that doesn't impl `Display` at all, unlike `str`.
173 /// #[derive(Debug)]
174 /// pub struct Oh;
175 /// assert!("oh no, an error: Oh" == &format!("{}", generate_error(Oh)));
176 /// ```
177 #[proc_macro_derive(
178 Display,
179 attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc)
180 )]
derive_error(input: TokenStream) -> TokenStream181 pub fn derive_error(input: TokenStream) -> TokenStream {
182 let input = parse_macro_input!(input as DeriveInput);
183 expand::derive(&input)
184 .unwrap_or_else(|err| err.to_compile_error())
185 .into()
186 }
187