enum_display_derive/
lib.rs

1//! # derive `Display` for simple enums
2//!
3//! You can derive the `Display` trait for simple enums.
4//!
5//! Actually, the most complex enum definition that this crate supports is like this one:
6//!
7//! ```rust,ignore
8//! #[derive(Display)]
9//! pub enum FooBar {
10//!     Foo,
11//!     Bar(),
12//!     FooBar(i32), // some wrapped type which implements Display
13//! }
14//! ```
15//!
16//! The above code will be expanded (roughly, without the enum definition) into this code:
17//!
18//! ```rust,ignore
19//! impl Display for FooBar {
20//!     fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
21//!         match *self {
22//!             FooBar::Foo => f.write_str("Foo"),
23//!             FooBar::Bar => f.write_str("Bar()"),
24//!             FooBar::FooBar(ref inner) => ::std::fmt::Display::fmt(inner, f),
25//!         }
26//!     }
27//! }
28//! ```
29//!
30//! ## Examples
31//!
32//! ```rust,ignore
33//! #[macro_use]
34//! extern crate enum_display_derive;
35//!
36//! use std::fmt::Display;
37//!
38//! #[derive(Display)]
39//! enum FizzBuzz {
40//!    Fizz,
41//!    Buzz,
42//!    FizzBuzz,
43//!    Number(u64),
44//! }
45//!
46//! fn fb(i: u64) {
47//!    match (i % 3, i % 5) {
48//!        (0, 0) => FizzBuzz::FizzBuzz,
49//!        (0, _) => FizzBuzz::Fizz,
50//!        (_, 0) => FizzBuzz::Buzz,
51//!        (_, _) => FizzBuzz::Number(i),
52//!    }
53//! }
54//!
55//! fn main() {
56//!    for i in 0..100 {
57//!        println!("{}", fb(i));
58//!    }
59//! }
60//! ```
61//!
62
63extern crate proc_macro;
64extern crate proc_macro2;
65extern crate syn;
66#[macro_use]
67extern crate quote;
68
69use proc_macro::TokenStream;
70
71#[proc_macro_derive(Display)]
72#[doc(hidden)]
73pub fn display(input: TokenStream) -> TokenStream {
74    // Parse the string representation
75    let ast: syn::DeriveInput = syn::parse(input).unwrap();
76
77    match ast.data {
78        syn::Data::Enum(ref enum_data) => {
79            let name = &ast.ident;
80            impl_display(name, enum_data).into()
81        }
82        _ => panic!("#[derive(Display)] works only on enums"),
83    }
84}
85
86fn impl_display(name: &syn::Ident, data: &syn::DataEnum) -> proc_macro2::TokenStream {
87    let variants = data.variants.iter()
88        .map(|variant| impl_display_for_variant(name, variant));
89
90    quote! {
91        impl Display for #name {
92            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
93                match *self {
94                    #(#variants)*
95                }
96            }
97        }
98    }
99}
100
101fn impl_display_for_variant(name: &syn::Ident, variant: &syn::Variant) -> proc_macro2::TokenStream {
102    let id = &variant.ident;
103    match variant.fields {
104        syn::Fields::Unit => {
105            quote! {
106                #name::#id => {
107                    f.write_str(stringify!(#id))
108                }
109            }
110        }
111        syn::Fields::Unnamed(ref fields) => {
112            match fields.unnamed.len() {
113                0 => {
114                    quote! {
115                        #name::#id() => {
116                            f.write_str(stringify!(#id))?;
117                            f.write_str("()")
118                        }
119                    }
120                }
121                1 => {
122                    quote! {
123                        #name::#id(ref inner) => {
124                            ::std::fmt::Display::fmt(inner, f)
125                        }
126                    }
127                }
128                _ => {
129                    panic!("#[derive(Display)] does not support tuple variants with more than one \
130                            fields")
131                }
132            }
133        }
134        _ => panic!("#[derive(Display)] works only with unit and tuple variants"),
135    }
136}