displaydoc/lib.rs
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
//! This library provides a convenient derive macro for the standard library's
//! [`core::fmt::Display`] trait.
//!
//! [`core::fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//!
//! ```toml
//! [dependencies]
//! displaydoc = "0.2"
//! ```
//!
//! *Compiler support: requires rustc 1.56+*
//!
//! <br>
//!
//! ## Example
//!
//! *Demonstration alongside the [`Error`][std::error::Error] derive macro from [`thiserror`](https://docs.rs/thiserror/1.0.25/thiserror/index.html),
//! to propagate source locations from [`io::Error`][std::io::Error] with the `#[source]` attribute:*
//! ```rust
//! use std::io;
//! use displaydoc::Display;
//! use thiserror::Error;
//!
//! #[derive(Display, Error, Debug)]
//! pub enum DataStoreError {
//! /// data store disconnected
//! Disconnect(#[source] io::Error),
//! /// the data for key `{0}` is not available
//! Redaction(String),
//! /// invalid header (expected {expected:?}, found {found:?})
//! InvalidHeader {
//! expected: String,
//! found: String,
//! },
//! /// unknown data store error
//! Unknown,
//! }
//!
//! let error = DataStoreError::Redaction("CLASSIFIED CONTENT".to_string());
//! assert!("the data for key `CLASSIFIED CONTENT` is not available" == &format!("{}", error));
//! ```
//! *Note that although [`io::Error`][std::io::Error] implements `Display`, we do not add it to the
//! generated message for `DataStoreError::Disconnect`, since it is already made available via
//! `#[source]`. See further context on avoiding duplication in error reports at the rust blog
//! [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).*
//!
//! <br>
//!
//! ## Details
//!
//! - A `fmt::Display` impl is generated for your enum if you provide
//! a docstring comment on each variant as shown above in the example. The
//! `Display` derive macro supports a shorthand for interpolating fields from
//! the error:
//! - `/// {var}` ⟶ `write!("{}", self.var)`
//! - `/// {0}` ⟶ `write!("{}", self.0)`
//! - `/// {var:?}` ⟶ `write!("{:?}", self.var)`
//! - `/// {0:?}` ⟶ `write!("{:?}", self.0)`
//! - This also works with structs and [generic types][crate::Display#generic-type-parameters]:
//! ```rust
//! # use displaydoc::Display;
//! /// oh no, an error: {0}
//! #[derive(Display)]
//! pub struct Error<E>(pub E);
//!
//! let error: Error<&str> = Error("muahaha i am an error");
//! assert!("oh no, an error: muahaha i am an error" == &format!("{}", error));
//! ```
//!
//! - Two optional attributes can be added to your types next to the derive:
//!
//! - `#[ignore_extra_doc_attributes]` makes the macro ignore any doc
//! comment attributes (or `///` lines) after the first. Multi-line
//! comments using `///` are otherwise treated as an error, so use this
//! attribute or consider switching to block doc comments (`/** */`).
//!
//! - `#[prefix_enum_doc_attributes]` combines the doc comment message on
//! your enum itself with the messages for each variant, in the format
//! “enum: variant”. When added to an enum, the doc comment on the enum
//! becomes mandatory. When added to any other type, it has no effect.
//!
//! - In case you want to have an independent doc comment, the
//! `#[displaydoc("...")` atrribute may be used on the variant or struct to
//! override it.
//!
//! <br>
//!
//! ## FAQ
//!
//! 1. **Is this crate `no_std` compatible?**
//! * 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`.
//!
//! 2. **Does this crate work with `Path` and `PathBuf` via the `Display` trait?**
//! * 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!
#![doc(html_root_url = "https://docs.rs/displaydoc/0.2.3")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(
rust_2018_idioms,
unreachable_pub,
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
#![allow(clippy::try_err)]
#[allow(unused_extern_crates)]
extern crate proc_macro;
mod attr;
mod expand;
mod fmt;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};
/// [Custom `#[derive(...)]` macro](https://doc.rust-lang.org/edition-guide/rust-2018/macros/custom-derive.html)
/// for implementing [`fmt::Display`][core::fmt::Display] via doc comment attributes.
///
/// ### Generic Type Parameters
///
/// Type parameters to an enum or struct using this macro should *not* need to
/// have an explicit `Display` constraint at the struct or enum definition
/// site. A `Display` implementation for the `derive`d struct or enum is
/// generated assuming each type parameter implements `Display`, but that should
/// be possible without adding the constraint to the struct definition itself:
/// ```rust
/// use displaydoc::Display;
///
/// /// oh no, an error: {0}
/// #[derive(Display)]
/// pub struct Error<E>(pub E);
///
/// // No need to require `E: Display`, since `displaydoc::Display` adds that implicitly.
/// fn generate_error<E>(e: E) -> Error<E> { Error(e) }
///
/// assert!("oh no, an error: muahaha" == &format!("{}", generate_error("muahaha")));
/// ```
///
/// ### Using [`Debug`][core::fmt::Debug] Implementations with Type Parameters
/// However, if a type parameter must instead be constrained with the
/// [`Debug`][core::fmt::Debug] trait so that some field may be printed with
/// `{:?}`, that constraint must currently still also be specified redundantly
/// at the struct or enum definition site. If a struct or enum field is being
/// formatted with `{:?}` via [`displaydoc`][crate], and a generic type
/// parameter must implement `Debug` to do that, then that struct or enum
/// definition will need to propagate the `Debug` constraint to every type
/// parameter it's instantiated with:
/// ```rust
/// use core::fmt::Debug;
/// use displaydoc::Display;
///
/// /// oh no, an error: {0:?}
/// #[derive(Display)]
/// pub struct Error<E: Debug>(pub E);
///
/// // `E: Debug` now has to propagate to callers.
/// fn generate_error<E: Debug>(e: E) -> Error<E> { Error(e) }
///
/// assert!("oh no, an error: \"cool\"" == &format!("{}", generate_error("cool")));
///
/// // Try this with a struct that doesn't impl `Display` at all, unlike `str`.
/// #[derive(Debug)]
/// pub struct Oh;
/// assert!("oh no, an error: Oh" == &format!("{}", generate_error(Oh)));
/// ```
#[proc_macro_derive(
Display,
attributes(ignore_extra_doc_attributes, prefix_enum_doc_attributes, displaydoc)
)]
pub fn derive_error(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
expand::derive(&input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}