1#[macro_export]
46macro_rules! forward_display {
47 (<$($generic:ident),+> in $this:ty => $field:ident) => {
48 impl <$($generic: ::core::fmt::Display),+> ::core::fmt::Display for $this {
49 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
50 ::core::fmt::Display::fmt(&self.$field, fmt)
51 }
52 }
53 };
54
55 (<$($generic:ident),+> in $this:ty) => {
56 impl <$($generic: ::core::fmt::Display),+> ::core::fmt::Display for $this {
57 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
58 ::core::fmt::Display::fmt(&self.0, fmt)
59 }
60 }
61 };
62
63 ($ty:ty) => {
64 impl ::core::fmt::Display for $ty {
65 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
66 ::core::fmt::Display::fmt(&self.0, fmt)
67 }
68 }
69 };
70
71 ($ty:ty => $field:ident) => {
72 impl ::core::fmt::Display for $ty {
73 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
74 ::core::fmt::Display::fmt(&self.$field, fmt)
75 }
76 }
77 };
78}
79
80#[macro_export]
110macro_rules! impl_display {
111 ($ty:ty: $format:literal) => {
113 impl ::core::fmt::Display for $ty {
114 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
115 ::core::write!(fmt, $format)
116 }
117 }
118 };
119
120 ($ty:ty: $format:literal, $($args:expr),+) => {
122 impl ::core::fmt::Display for $ty {
123 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
124 ::core::write!(fmt, $format, $($args),+)
125 }
126 }
127 };
128
129 ($ty:ty: $format:literal, $($args:expr),+ ,) => {
131 $crate::impl_display!($ty: $format, $($args),+);
132 };
133}
134
135#[macro_export]
206macro_rules! impl_display_enum {
207 (
208 $ty:ty:
209 $(
210 $variant:ident
211 $( ( $($tuple:tt)* ) )?
212 $( { $($named:tt)* } )?
213 => $output:tt
214 ),+
215 $(,)?
216 ) => {
217 impl ::core::fmt::Display for $ty {
218 fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
219 match self {
220 $(
221 Self::$variant
222 $( ( $($tuple)* ) )?
223 $( { $($named)* } )?
224 => $crate::impl_display_enum!(
225 @write fmt
226 [
227 $( ( $($tuple)* ) )?
228 $( { $($named)* } )?
229 ]
230 $output
231 ),
232 )+
233 }
234 }
235 }
236 };
237
238 (
240 @write $fmt:ident
241 []
242 $text:literal
243 ) => {
244 $fmt.write_str($text)
245 };
246
247 (
248 @write $fmt:ident
249 [ $($fields:tt)* ]
250 $format:literal
251 ) => {
252 ::core::write!($fmt, $format)
253 };
254
255 (
256 @write $fmt:ident
257 [ $($fields:tt)* ]
258 ( $format:literal $($args:tt)* )
259 ) => {
260 ::core::write!($fmt, $format $($args)*)
261 };
262}
263
264#[cfg(test)]
265mod tests {
266 use alloc::{
267 borrow::ToOwned as _,
268 string::{String, ToString as _},
269 };
270
271 #[test]
272 fn impl_forward_for_newtype_struct() {
273 struct Foo(String);
274
275 forward_display!(Foo);
276
277 assert_eq!(Foo("hello world".to_owned()).to_string(), "hello world");
278 }
279
280 #[test]
281 fn impl_forward_newtype_named_struct() {
282 struct Foo {
283 inner: u64,
284 }
285
286 forward_display!(Foo => inner);
287
288 assert_eq!(Foo { inner: 42 }.to_string(), "42");
289 }
290
291 #[test]
292 fn impl_forward_generic_newtype_struct() {
293 struct Foo<T>(T);
294
295 forward_display!(<T> in Foo<T>);
296
297 assert_eq!(Foo(42).to_string(), "42");
298 }
299
300 #[test]
301 fn impl_forward_generic_named_struct() {
302 struct Foo<T> {
303 inner: T,
304 }
305
306 forward_display!(<T> in Foo<T> => inner);
307
308 assert_eq!(Foo { inner: 42 }.to_string(), "42");
309 }
310
311 #[test]
312 fn impl_basic_for_unit_struct() {
313 struct Foo;
314 impl_display!(Foo: "foo");
315 assert_eq!(Foo.to_string(), "foo");
316 }
317
318 #[test]
319 fn impl_basic_with_args() {
320 struct Foo;
321 impl_display!(Foo: "foo {} {}", 2, 3);
322 assert_eq!(Foo.to_string(), "foo 2 3");
323 }
324
325 #[test]
326 fn impl_basic_with_inline_args() {
327 const HI: &str = "hello";
328
329 struct Hello3;
330 impl_display!(Hello3: "{HI} world");
331 assert_eq!(Hello3.to_string(), "hello world");
332 }
333
334 #[test]
335 fn impl_enum_named_variant_with_trailing_comma() {
336 enum Foo {
337 Bar { value: u64 },
338 }
339
340 impl_display_enum!(Foo: Bar { value } => "{value}",);
341
342 assert_eq!(Foo::Bar { value: 42 }.to_string(), "42");
343 }
344
345 #[test]
346 fn enum_mixed_patterns_and_format_arguments() {
347 #[allow(dead_code)] enum Event {
349 Idle,
350 Message(&'static str, bool),
351 Progress { completed: usize, total: usize },
352 Items(&'static [u8]),
353 Done,
354 }
355
356 impl_display_enum! {
357 Event:
358 Idle => "{{idle}}",
359 Message(msg, ..) => "{{{msg}}}",
360 Progress { completed: count, .. } => "{count} complete",
361 Items(items) => ("{} items ({label})", items.len(), label = "ready",),
362 Done => ("done: {}", 42),
363 }
364
365 assert_eq!(Event::Idle.to_string(), "{{idle}}");
366 assert_eq!(Event::Message("hi", true).to_string(), "{hi}");
367 assert_eq!(
368 Event::Progress {
369 completed: 2,
370 total: 3
371 }
372 .to_string(),
373 "2 complete"
374 );
375 assert_eq!(Event::Items(&[1, 2]).to_string(), "2 items (ready)");
376 assert_eq!(Event::Done.to_string(), "done: 42");
377 }
378
379 #[test]
380 fn enum_format_arguments_without_trailing_comma() {
381 #[allow(dead_code)] enum Value {
383 Tuple(u8, u8),
384 Named { value: u8 },
385 }
386
387 impl_display_enum!(Value:
388 Tuple(_, value) => "{value:02x}",
389 Named { value } => ("{number:02x}", number = value)
390 );
391
392 assert_eq!(Value::Tuple(0, 10).to_string(), "0a");
393 assert_eq!(Value::Named { value: 11 }.to_string(), "0b");
394
395 enum Count {
396 Items(&'static [u8]),
397 }
398 impl_display_enum!(Count: Items(items) => ("{}", items.len()));
399 assert_eq!(Count::Items(&[1, 2]).to_string(), "2");
400 }
401
402 #[test]
403 fn enum_grouped_unit_string_supports_captures() {
404 const LABEL: &str = "ready";
405 enum State {
406 Bare,
407 Grouped,
408 }
409 impl_display_enum!(State:
410 Bare => "{LABEL}",
411 Grouped => ("{LABEL}"),
412 );
413 assert_eq!(State::Bare.to_string(), "{LABEL}");
414 assert_eq!(State::Grouped.to_string(), "ready");
415 }
416
417 #[test]
418 fn enum_propagates_format_errors() {
419 struct Fails;
420 impl core::fmt::Display for Fails {
421 fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
422 Err(core::fmt::Error)
423 }
424 }
425
426 enum Value {
427 Tuple(Fails),
428 Named { value: Fails },
429 }
430 impl_display_enum!(Value: Tuple(value) => "{value}", Named { value } => "{value}");
431
432 let mut output = String::new();
433 core::fmt::write(&mut output, format_args!("{}", Value::Tuple(Fails))).unwrap_err();
434 core::fmt::write(
435 &mut output,
436 format_args!("{}", Value::Named { value: Fails }),
437 )
438 .unwrap_err();
439 }
440}