//! 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.45+*
//!
//! <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,
    const_err,
    dead_code,
    improper_ctypes,
    non_shorthand_field_patterns,
    no_mangle_generic_items,
    overflowing_literals,
    path_statements,
    patterns_in_fns_without_body,
    private_in_public,
    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()
}
