| 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 | |