| 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 | )] | 
|---|
| 181 | pub fn derive_error(input: TokenStream) -> TokenStream { | 
|---|
| 182 | let input: DeriveInput = parse_macro_input!(input as DeriveInput); | 
|---|
| 183 | expandTokenStream::derive(&input) | 
|---|
| 184 | .unwrap_or_else(|err: Error| err.to_compile_error()) | 
|---|
| 185 | .into() | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|