#![no_std]
#![forbid(unsafe_code)]
#![deny(rust_2018_idioms, missing_docs, clippy::pedantic)]
#[doc(hidden)]
pub mod __private {
#[doc(hidden)]
pub use displaydoc_lite_proc_macros as proc_macros;
}
#[macro_export]
macro_rules! displaydoc {
($(#[$enum_attr:meta])*
$pub:vis enum $name:ident {
$($body:tt)*
}) => {
$(#[$enum_attr])*
$pub enum $name { $($body)* }
$crate::__parse_enum_variant__! { enum $name { $($body)* } }
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __parse_enum_variant__ {
(@data $name:ident $this:ident $f:ident @variant $(#[$attr:meta])* $variant:ident (
$(
$( #[$field_meta:meta] )*
$field_vis:vis $field_ty:ty
),* $(,)?
) $(, $($tt:tt)* )? ) => {
#[allow(unused, clippy::used_underscore_binding)]
if let $crate::__private::proc_macros::__tuple_bindings__!($name, $variant, $($field_ty,)*) = $this {
$crate::__defile_expr__! {
$crate::__get_doc_string__!(@@struct $f, $(#[@$attr])*)
}
} else {
$crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
}
};
(@data $name:ident $this:ident $f:ident @variant $(#[$attr:meta])* $variant:ident {
$(
$( #[$field_meta:meta] )*
$field_vis:vis $field_name:ident : $field_ty:ty
),* $(,)?
} $(, $($tt:tt)* )? ) => {
#[allow(unused)]
if let $name::$variant { $($field_name),* } = $this {
$crate::__defile_expr__! {
$crate::__get_doc_string__!(@@struct $f, $(#[@$attr])*)
}
} else {
$crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
}
};
(@data $name:ident $this:ident $f:ident @variant
$( #[$field_meta:meta] )*
$variant:ident $(, $($tt:tt)* )?
) => {
if let $name::$variant = $this {
$crate::__defile_expr__! {
$crate::__get_doc_string__!(@@unit $f, $(#[@$field_meta])*)
}
} else {
$crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
}
};
(@data $_:ident $__:ident $___:ident @variant ,) => { unreachable!() };
(@data $_:ident $__:ident $___:ident @variant) => { unreachable!() };
(
$( #[$meta:meta] )*
$vis:vis enum $name:ident {
$($tt:tt)*
}
) => {
impl ::core::fmt::Display for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
$crate::__parse_enum_variant__!(@data $name self f @variant $($tt)*)
}
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __token_or_ok__ {
($x:expr) => {
$x
};
() => {
::core::result::Result::Ok(())
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __get_doc_string__ {
(@unit $f:ident, #[doc = $($doc:tt)*] $($rest:tt)*) => { $f.write_str(($($doc)*).trim()) };
(@unit $f:ident, #[$_:meta] $($rest:tt)*) => { $crate::__get_doc_string__!($f, $($rest)*) };
(@unit $f:ident,) => { Ok(()) };
(@struct $f:ident, #[doc = $($doc:tt)*] $($rest:tt)*) => {
$crate::__private::proc_macros::__struct_string__!($f, $($doc)*)
};
(@struct $f:ident, #[$_:meta] $($rest:tt)*) => { $crate::__get_doc_string__!($f, $($rest)*) };
(@struct $f:ident,) => { Ok(()) };
}
#[macro_export]
macro_rules! __defile_expr__ {
( $($input:tt)* ) => (
#[allow(non_camel_case_types)]
{
#[derive($crate::__private::proc_macros::__expr_hack__)]
enum __defile__Hack__ {
__defile__Hack__ = (stringify!($($input)*), 42).1
}
__defile__Hack__!()
}
)
}
#[cfg(test)]
mod tests {
extern crate std;
use std::string::{String, ToString};
use super::displaydoc;
displaydoc! {
#[derive(Debug)]
enum Error {
Foo,
Bar { s: u8, x: String },
Baz(u8, u16, u32),
Debug(String),
}
}
#[test]
fn it_works() {
assert_eq!(Error::Foo.to_string(), "Hello");
assert_eq!(
Error::Bar {
s: 0,
x: String::from("hello")
}
.to_string(),
"0 is hello"
);
assert_eq!(Error::Baz(0, 1, 2).to_string(), "0 tuple 2 works too 1");
assert_eq!(Error::Debug("hallo".into()).to_string(), "debug: \"hallo\"");
}
}