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 | private_in_public,
|
109 | unconditional_recursion,
|
110 | unused,
|
111 | unused_allocation,
|
112 | unused_comparisons,
|
113 | unused_parens,
|
114 | while_true
|
115 | )]
|
116 | #![allow (clippy::try_err)]
|
117 |
|
118 | #[allow (unused_extern_crates)]
|
119 | extern crate proc_macro;
|
120 |
|
121 | mod attr;
|
122 | mod expand;
|
123 | mod fmt;
|
124 |
|
125 | use proc_macro::TokenStream;
|
126 | use syn::{parse_macro_input, DeriveInput};
|
127 |
|
128 | /// [Custom `#[derive(...)]` macro](https://doc.rust-lang.org/edition-guide/rust-2018/macros/custom-derive.html)
|
129 | /// for implementing [`fmt::Display`][core::fmt::Display] via doc comment attributes.
|
130 | ///
|
131 | /// ### Generic Type Parameters
|
132 | ///
|
133 | /// Type parameters to an enum or struct using this macro should *not* need to
|
134 | /// have an explicit `Display` constraint at the struct or enum definition
|
135 | /// site. A `Display` implementation for the `derive`d struct or enum is
|
136 | /// generated assuming each type parameter implements `Display`, but that should
|
137 | /// be possible without adding the constraint to the struct definition itself:
|
138 | /// ```rust
|
139 | /// use displaydoc::Display;
|
140 | ///
|
141 | /// /// oh no, an error: {0}
|
142 | /// #[derive(Display)]
|
143 | /// pub struct Error<E>(pub E);
|
144 | ///
|
145 | /// // No need to require `E: Display`, since `displaydoc::Display` adds that implicitly.
|
146 | /// fn generate_error<E>(e: E) -> Error<E> { Error(e) }
|
147 | ///
|
148 | /// assert!("oh no, an error: muahaha" == &format!("{}" , generate_error("muahaha" )));
|
149 | /// ```
|
150 | ///
|
151 | /// ### Using [`Debug`][core::fmt::Debug] Implementations with Type Parameters
|
152 | /// However, if a type parameter must instead be constrained with the
|
153 | /// [`Debug`][core::fmt::Debug] trait so that some field may be printed with
|
154 | /// `{:?}`, that constraint must currently still also be specified redundantly
|
155 | /// at the struct or enum definition site. If a struct or enum field is being
|
156 | /// formatted with `{:?}` via [`displaydoc`][crate], and a generic type
|
157 | /// parameter must implement `Debug` to do that, then that struct or enum
|
158 | /// definition will need to propagate the `Debug` constraint to every type
|
159 | /// parameter it's instantiated with:
|
160 | /// ```rust
|
161 | /// use core::fmt::Debug;
|
162 | /// use displaydoc::Display;
|
163 | ///
|
164 | /// /// oh no, an error: {0:?}
|
165 | /// #[derive(Display)]
|
166 | /// pub struct Error<E: Debug>(pub E);
|
167 | ///
|
168 | /// // `E: Debug` now has to propagate to callers.
|
169 | /// fn generate_error<E: Debug>(e: E) -> Error<E> { Error(e) }
|
170 | ///
|
171 | /// assert!("oh no, an error: \"cool \"" == &format!("{}" , generate_error("cool" )));
|
172 | ///
|
173 | /// // Try this with a struct that doesn't impl `Display` at all, unlike `str`.
|
174 | /// #[derive(Debug)]
|
175 | /// pub struct Oh;
|
176 | /// assert!("oh no, an error: Oh" == &format!("{}" , generate_error(Oh)));
|
177 | /// ```
|
178 | #[proc_macro_derive (
|
179 | Display,
|
180 | attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc)
|
181 | )]
|
182 | pub fn derive_error(input: TokenStream) -> TokenStream {
|
183 | let input: DeriveInput = parse_macro_input!(input as DeriveInput);
|
184 | expandTokenStream::derive(&input)
|
185 | .unwrap_or_else(|err: Error| err.to_compile_error())
|
186 | .into()
|
187 | }
|
188 | |