1#![no_std]
57#![forbid(unsafe_code)]
58#![deny(rust_2018_idioms, missing_docs, clippy::pedantic)]
59
60#[doc(hidden)]
61pub mod __private {
62 #[doc(hidden)]
63 pub use displaydoc_lite_proc_macros as proc_macros;
64}
65
66#[macro_export]
71macro_rules! displaydoc {
72 ($(#[$enum_attr:meta])*
73 $pub:vis enum $name:ident {
74 $($body:tt)*
75 }) => {
76 $(#[$enum_attr])*
77 $pub enum $name { $($body)* }
78
79 $crate::__parse_enum_variant__! { enum $name { $($body)* } }
80 };
81}
82
83#[macro_export]
84#[doc(hidden)]
85macro_rules! __parse_enum_variant__ {
86 (@data $name:ident $this:ident $f:ident @variant $(#[$attr:meta])* $variant:ident (
88 $(
89 $( #[$field_meta:meta] )*
90 $field_vis:vis $field_ty:ty
91 ),* $(,)?
92 ) $(, $($tt:tt)* )? ) => {
93 #[allow(unused, clippy::used_underscore_binding)]
94 if let $crate::__private::proc_macros::__tuple_bindings__!($name, $variant, $($field_ty,)*) = $this {
95 $crate::__defile_expr__! {
96 $crate::__get_doc_string__!(@@struct $f, $(#[@$attr])*)
97 }
98 } else {
99 $crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
101 }
102 };
103
104 (@data $name:ident $this:ident $f:ident @variant $(#[$attr:meta])* $variant:ident {
106 $(
107 $( #[$field_meta:meta] )*
108 $field_vis:vis $field_name:ident : $field_ty:ty
109 ),* $(,)?
110 } $(, $($tt:tt)* )? ) => {
111 #[allow(unused)]
112 if let $name::$variant { $($field_name),* } = $this {
113 $crate::__defile_expr__! {
114 $crate::__get_doc_string__!(@@struct $f, $(#[@$attr])*)
115 }
116 } else {
117 $crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
119 }
120 };
121
122 (@data $name:ident $this:ident $f:ident @variant
124 $( #[$field_meta:meta] )*
125 $variant:ident $(, $($tt:tt)* )?
126 ) => {
127 if let $name::$variant = $this {
128 $crate::__defile_expr__! {
129 $crate::__get_doc_string__!(@@unit $f, $(#[@$field_meta])*)
130 }
131 } else {
132 $crate::__token_or_ok__!( $( $crate::__parse_enum_variant__!(@data $name $this $f @variant $( $tt )*) )? )
134 }
135 };
136
137 (@data $_:ident $__:ident $___:ident @variant ,) => { unreachable!() };
139
140 (@data $_:ident $__:ident $___:ident @variant) => { unreachable!() };
142
143 (
145 $( #[$meta:meta] )*
146 $vis:vis enum $name:ident {
147 $($tt:tt)*
148 }
149 ) => {
150 impl ::core::fmt::Display for $name {
151 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
152 $crate::__parse_enum_variant__!(@data $name self f @variant $($tt)*)
153 }
154 }
155 };
156}
157
158#[macro_export]
159#[doc(hidden)]
160macro_rules! __token_or_ok__ {
161 ($x:expr) => {
162 $x
163 };
164
165 () => {
166 ::core::result::Result::Ok(())
167 };
168}
169
170#[macro_export]
171#[doc(hidden)]
172macro_rules! __get_doc_string__ {
173 (@unit $f:ident, #[doc = $($doc:tt)*] $($rest:tt)*) => { $f.write_str(($($doc)*).trim()) };
174 (@unit $f:ident, #[$_:meta] $($rest:tt)*) => { $crate::__get_doc_string__!($f, $($rest)*) };
175 (@unit $f:ident,) => { Ok(()) };
176
177 (@struct $f:ident, #[doc = $($doc:tt)*] $($rest:tt)*) => {
178 $crate::__private::proc_macros::__struct_string__!($f, $($doc)*)
179 };
180 (@struct $f:ident, #[$_:meta] $($rest:tt)*) => { $crate::__get_doc_string__!($f, $($rest)*) };
181 (@struct $f:ident,) => { Ok(()) };
182}
183
184#[macro_export]
186macro_rules! __defile_expr__ {
187 ( $($input:tt)* ) => (
188 #[allow(non_camel_case_types)]
189 {
190 #[derive($crate::__private::proc_macros::__expr_hack__)]
191 enum __defile__Hack__ {
192 __defile__Hack__ = (stringify!($($input)*), 42).1
193 }
194 __defile__Hack__!()
195 }
196 )
197}
198
199#[cfg(test)]
200mod tests {
201 extern crate std;
202 use std::string::{String, ToString};
203
204 use super::displaydoc;
205
206 displaydoc! {
207 #[derive(Debug)]
209 enum Error {
210 Foo,
214 Bar { s: u8, x: String },
216 Baz(u8, u16, u32),
218 Debug(String),
220 }
221 }
222
223 #[test]
224 fn it_works() {
225 assert_eq!(Error::Foo.to_string(), "Hello");
226 assert_eq!(
227 Error::Bar {
228 s: 0,
229 x: String::from("hello")
230 }
231 .to_string(),
232 "0 is hello"
233 );
234 assert_eq!(Error::Baz(0, 1, 2).to_string(), "0 tuple 2 works too 1");
235 assert_eq!(Error::Debug("hallo".into()).to_string(), "debug: \"hallo\"");
236 }
237}