Add derive for 'UriDisplay'.

This commit is contained in:
Divyahans Gupta 2018-10-24 22:14:05 -07:00 committed by Sergio Benitez
parent 34421f13f3
commit cda4c520f1
5 changed files with 71 additions and 21 deletions

View File

@ -1,3 +1,4 @@
pub mod from_form;
pub mod from_form_value;
pub mod responder;
pub mod uri_display;

View File

@ -0,0 +1,61 @@
use proc_macro::{Span, TokenStream};
use derive_utils::*;
const NO_EMPTY_FIELDS: &str = "fieldless structs or variants are not allowed";
const NO_NULLARY: &str = "nullary items are not allowed";
const NO_EMPTY_ENUMS: &str = "empty enums are not allowed";
const ONLY_ONE_UNNAMED: &str = "tuple structs or variants must have exactly one field";
fn validate_fields(fields: Fields, parent_span: Span) -> Result<()> {
if fields.count() == 0 {
return Err(parent_span.error(NO_EMPTY_FIELDS))
} else if fields.are_unnamed() && fields.count() > 1 {
return Err(fields.span().error(ONLY_ONE_UNNAMED));
} else if fields.are_unit() {
return Err(parent_span.error(NO_NULLARY));
}
Ok(())
}
fn validate_struct(gen: &DeriveGenerator, data: Struct) -> Result<()> {
validate_fields(data.fields(), gen.input.span())
}
fn validate_enum(gen: &DeriveGenerator, data: Enum) -> Result<()> {
if data.variants().count() == 0 {
return Err(gen.input.span().error(NO_EMPTY_ENUMS));
}
for variant in data.variants() {
validate_fields(variant.fields(), variant.span())?;
}
Ok(())
}
pub fn derive_uri_display(input: TokenStream) -> TokenStream {
DeriveGenerator::build_for(input, "::rocket::http::uri::UriDisplay")
.generic_support(GenericSupport::Type | GenericSupport::Lifetime)
.data_support(DataSupport::Struct | DataSupport::Enum)
.validate_enum(validate_enum)
.validate_struct(validate_struct)
.map_type_generic(|_, ident, _| quote!(#ident : ::rocket::http::uri::UriDisplay))
.function(|_, inner| quote! {
fn fmt(&self, f: &mut ::rocket::http::uri::Formatter) -> ::std::fmt::Result {
#inner
Ok(())
}
})
.map_field(|_, field| {
let span = field.span().into();
let accessor = field.accessor();
if let Some(ref ident) = field.ident {
let name = ident.to_string();
quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
} else {
quote_spanned!(span => f.write_value(&#accessor)?;)
}
})
.to_tokens()
}

View File

@ -583,6 +583,11 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
emit!(derive::responder::derive_responder(input))
}
#[proc_macro_derive(UriDisplay)]
pub fn derive_uri_display(input: TokenStream) -> TokenStream {
emit!(derive::uri_display::derive_uri_display(input))
}
/// Generates a [`Vec`] of [`Route`]s from a set of route paths.
///
/// The `routes!` macro expands a list of route paths into a [`Vec`] of their

View File

@ -2,7 +2,6 @@
#[macro_use] extern crate rocket;
use std::fmt;
use std::path::PathBuf;
use rocket::{Request, Outcome::*};
@ -10,22 +9,15 @@ use rocket::http::ext::Normalize;
use rocket::local::Client;
use rocket::data::{self, Data, FromDataSimple};
use rocket::request::Form;
use rocket::http::{Status, RawStr, ContentType, uri::{Formatter, UriDisplay}};
use rocket::http::{Status, RawStr, ContentType};
// Use all of the code generation avaiable at once.
#[derive(FromForm)]
#[derive(FromForm, UriDisplay)]
struct Inner<'r> {
field: &'r RawStr
}
// TODO: Make this deriveable.
impl<'a> UriDisplay for Inner<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_named_value("field", &self.field)
}
}
struct Simple(String);
impl FromDataSimple for Simple {

View File

@ -3,27 +3,18 @@
#[macro_use] extern crate rocket;
use std::fmt;
use std::path::PathBuf;
use rocket::http::{RawStr, Cookies};
use rocket::http::uri::{Origin, Formatter, UriDisplay, FromUriParam};
use rocket::http::uri::{Origin, FromUriParam};
use rocket::request::Form;
#[derive(FromForm)]
#[derive(FromForm, UriDisplay)]
struct User<'a> {
name: &'a RawStr,
nickname: String,
}
// TODO: Make this deriveable.
impl<'a> UriDisplay for User<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_named_value("name", &self.name)?;
f.write_named_value("nickname", &self.nickname)
}
}
impl<'a, 'b> FromUriParam<(&'a str, &'b str)> for User<'a> {
type Target = User<'a>;
fn from_uri_param((name, nickname): (&'a str, &'b str)) -> User<'a> {