#![feature(core_intrinsics, decl_macro)] #![feature(proc_macro_diagnostic, proc_macro_span)] #![recursion_limit="256"] extern crate syn; extern crate proc_macro; extern crate proc_macro2; #[macro_use] extern crate quote; mod parser; mod spanned; mod ext; use parser::Result as PResult; use proc_macro::{Span, TokenStream}; use spanned::Spanned; use ext::*; use syn::*; const NO_FIELDS_ERR: &str = "variants in `FromFormValue` derives cannot have fields"; const NO_GENERICS: &str = "enums with generics cannot derive `FromFormValue`"; const ONLY_ENUMS: &str = "`FromFormValue` can only be derived for enums"; const EMPTY_ENUM_WARN: &str = "deriving `FromFormValue` for empty enum"; fn validate_input(input: DeriveInput) -> PResult { // This derive doesn't support generics. Error out if there are generics. if !input.generics.params.is_empty() { return Err(input.generics.span().error(NO_GENERICS)); } // This derive only works for enums. Error out if the input is not an enum. let input_span = input.span(); let data = input.data.into_enum().ok_or_else(|| input_span.error(ONLY_ENUMS))?; // This derive only works for variants that are nullary. for variant in data.variants.iter() { if !variant.fields.is_empty() { return Err(variant.span().error(NO_FIELDS_ERR)); } } // Emit a warning if the enum is empty. if data.variants.is_empty() { Span::call_site().warning(EMPTY_ENUM_WARN).emit(); } Ok(data) } fn real_derive_from_form_value(input: TokenStream) -> PResult { // Parse the input `TokenStream` as a `syn::DeriveInput`, an AST. let input: DeriveInput = syn::parse(input).map_err(|e| { Span::call_site().error(format!("error: failed to parse input: {:?}", e)) })?; // Validate the enum. let name = input.ident.clone(); let enum_data = validate_input(input)?; // Create iterators over the identifers as idents and as strings. let variant_strs = enum_data.variants.iter().map(|v| v.ident.to_string()); let variant_idents = enum_data.variants.iter().map(|v| &v.ident); let names = ::std::iter::repeat(&name); // Generate the implementation. Ok(quote! { impl<'v> ::rocket::request::FromFormValue<'v> for #name { type Error = &'v ::rocket::http::RawStr; fn from_form_value(v: &'v ::rocket::http::RawStr) -> ::std::result::Result { #(if v.as_uncased_str() == #variant_strs { return ::std::result::Result::Ok(#names::#variant_idents); })* ::std::result::Result::Err(v) } } }.into()) } #[proc_macro_derive(FromFormValue)] pub fn derive_from_form_value(input: TokenStream) -> TokenStream { real_derive_from_form_value(input).unwrap_or_else(|diag| { diag.emit(); TokenStream::new() }) }