1 #![feature(proc_macro_diagnostic)]
2 
3 #[macro_use] extern crate quote;
4 extern crate devise;
5 extern crate proc_macro;
6 
7 use proc_macro::{TokenStream, Span};
8 use devise::*;
9 
10 const NO_EMPTY_FIELDS: &str = "fieldless structs or variants are not allowed";
11 const NO_NULLARY: &str = "nullary items are not allowed";
12 const NO_EMPTY_ENUMS: &str = "empty enums are not allowed";
13 const ONLY_ONE_UNNAMED: &str = "tuple structs or variants must have exactly one field";
14 
validate_fields(fields: Fields, parent_span: Span) -> Result<()>15 fn validate_fields(fields: Fields, parent_span: Span) -> Result<()> {
16     if fields.count() == 0 {
17         return Err(parent_span.error(NO_EMPTY_FIELDS))
18     } else if fields.are_unnamed() && fields.count() > 1 {
19         return Err(fields.span().error(ONLY_ONE_UNNAMED));
20     } else if fields.are_unit() {
21         return Err(parent_span.error(NO_NULLARY));
22     }
23 
24     Ok(())
25 }
26 
validate_struct(gen: &DeriveGenerator, data: Struct) -> Result<()>27 fn validate_struct(gen: &DeriveGenerator, data: Struct) -> Result<()> {
28     validate_fields(data.fields(), gen.input.span())
29 }
30 
validate_enum(gen: &DeriveGenerator, data: Enum) -> Result<()>31 fn validate_enum(gen: &DeriveGenerator, data: Enum) -> Result<()> {
32     if data.variants().count() == 0 {
33         return Err(gen.input.span().error(NO_EMPTY_ENUMS));
34     }
35 
36     for variant in data.variants() {
37         validate_fields(variant.fields(), variant.span())?;
38     }
39 
40     Ok(())
41 }
42 
43 #[proc_macro_derive(UriDisplay)]
derive_uri_display(input: TokenStream) -> TokenStream44 pub fn derive_uri_display(input: TokenStream) -> TokenStream {
45     // DeriveGenerator::build_for(input, "::rocket::uri::UriDisplay")
46     DeriveGenerator::build_for(input, quote!(impl UriDisplay))
47         .generic_support(GenericSupport::Type | GenericSupport::Lifetime)
48         .data_support(DataSupport::Struct | DataSupport::Enum)
49         .validate_enum(validate_enum)
50         .validate_struct(validate_struct)
51         // .map_type_generic(|_, ident, _| quote!(#ident : ::rocket::uri::UriDisplay))
52         .map_type_generic(|_, ident, _| quote!(#ident : UriDisplay))
53         .function(|_, inner| quote! {
54             // fn fmt(&self, f: &mut ::rocket::uri::Formatter) -> ::std::fmt::Result {
55             fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
56                 #inner
57                 Ok(())
58             }
59         })
60         .map_field(|_, field| {
61             let span = field.span().into();
62             let accessor = field.accessor();
63             if let Some(ref ident) = field.ident {
64                 let name = ident.to_string();
65                 quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
66             } else {
67                 quote_spanned!(span => f.write_value(&#accessor)?;)
68             }
69         })
70         .to_tokens()
71 }
72