Update request/response header logic for new style.

This commit is contained in:
Jimmy Cuadra 2018-05-16 00:40:51 -07:00
parent f0f4f9bd17
commit c9454caff1
5 changed files with 87 additions and 28 deletions

View File

@ -10,7 +10,7 @@ You define the endpoint's metadata, request fields, and response fields, and the
Here is an example that shows most of the macro's functionality.
``` rust
#![feature(associated_consts, proc_macro, try_from)]
#![feature(proc_macro, try_from)]
extern crate futures;
extern crate http;

View File

@ -99,7 +99,7 @@ impl ToTokens for Api {
});
} else {
tokens.append_all(quote! {
("#segment");
(#segment);
});
}
}
@ -125,6 +125,18 @@ impl ToTokens for Api {
Tokens::new()
};
let add_headers_to_request = if self.request.has_header_fields() {
let mut header_tokens = quote! {
let headers = http_request.headers_mut();
};
header_tokens.append_all(self.request.add_headers_to_request());
header_tokens
} else {
Tokens::new()
};
let add_body_to_request = if let Some(field) = self.request.newtype_body_field() {
let field_name = field.ident.expect("expected field to have an identifier");
@ -211,7 +223,7 @@ impl ToTokens for Api {
#request_types
impl ::std::convert::TryFrom<Request> for ::http::Request {
impl<T> ::std::convert::TryFrom<Request> for ::http::Request<T> {
type Error = ::ruma_api::Error;
#[allow(unused_mut, unused_variables)]
@ -232,6 +244,8 @@ impl ToTokens for Api {
url.into_string().parse().unwrap(),
);
{ #add_headers_to_request }
{ #add_body_to_request }
Ok(http_request)
@ -240,12 +254,12 @@ impl ToTokens for Api {
#response_types
impl ::futures::future::FutureFrom<::http::Response> for Response {
impl<T> ::futures::future::FutureFrom<::http::Response<T>> for Response {
type Future = Box<_Future<Item = Self, Error = Self::Error>>;
type Error = ::ruma_api::Error;
#[allow(unused_variables)]
fn future_from(http_response: ::http::Response)
fn future_from(http_response: ::http::Response<T>)
-> Box<_Future<Item = Self, Error = Self::Error>> {
#extract_headers

View File

@ -1,6 +1,6 @@
use quote::{ToTokens, Tokens};
use syn::spanned::Spanned;
use syn::{Field, Meta, NestedMeta};
use syn::{Field, Ident, Lit, Meta, NestedMeta};
use api::strip_serde_attrs;
@ -9,10 +9,31 @@ pub struct Request {
}
impl Request {
pub fn add_headers_to_request(&self) -> Tokens {
self.header_fields().fold(Tokens::new(), |mut header_tokens, request_field| {
let (field, header_name_string) = match request_field {
RequestField::Header(field, header_name_string) => (field, header_name_string),
_ => panic!("expected request field to be header variant"),
};
let field_name = &field.ident;
let header_name = Ident::from(header_name_string.as_ref());
header_tokens.append_all(quote! {
headers.append(::http::header::#header_name, request.#field_name);
});
header_tokens
})
}
pub fn has_body_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_body())
}
pub fn has_header_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_header())
}
pub fn has_path_fields(&self) -> bool {
self.fields.iter().any(|field| field.is_path())
}
@ -21,6 +42,10 @@ impl Request {
self.fields.iter().any(|field| field.is_query())
}
pub fn header_fields(&self) -> impl Iterator<Item = &RequestField> {
self.fields.iter().filter(|field| field.is_header())
}
pub fn path_field_count(&self) -> usize {
self.fields.iter().filter(|field| field.is_path()).count()
}
@ -72,6 +97,7 @@ impl From<Vec<Field>> for Request {
let fields = fields.into_iter().map(|mut field| {
let mut field_kind = RequestFieldKind::Body;
let mut header = None;
field.attrs = field.attrs.into_iter().filter(|attr| {
let meta = attr.interpret_meta()
@ -103,7 +129,14 @@ impl From<Vec<Field>> for Request {
}
Meta::NameValue(name_value) => {
match name_value.ident.as_ref() {
"header" => field_kind = RequestFieldKind::Header,
"header" => {
match name_value.lit {
Lit::Str(lit_str) => header = Some(lit_str.value()),
_ => panic!("ruma_api! header attribute's value must be a string literal"),
}
field_kind = RequestFieldKind::Header;
}
_ => panic!("ruma_api! name/value pair attribute on requests must be: header"),
}
}
@ -126,7 +159,7 @@ impl From<Vec<Field>> for Request {
);
}
RequestField::new(field_kind, field)
RequestField::new(field_kind, field, header)
}).collect();
Request {
@ -267,17 +300,17 @@ impl ToTokens for Request {
pub enum RequestField {
Body(Field),
Header(Field),
Header(Field, String),
NewtypeBody(Field),
Path(Field),
Query(Field),
}
impl RequestField {
fn new(kind: RequestFieldKind, field: Field) -> RequestField {
fn new(kind: RequestFieldKind, field: Field, header: Option<String>) -> RequestField {
match kind {
RequestFieldKind::Body => RequestField::Body(field),
RequestFieldKind::Header => RequestField::Header(field),
RequestFieldKind::Header => RequestField::Header(field, header.expect("missing header name")),
RequestFieldKind::NewtypeBody => RequestField::NewtypeBody(field),
RequestFieldKind::Path => RequestField::Path(field),
RequestFieldKind::Query => RequestField::Query(field),
@ -286,11 +319,11 @@ impl RequestField {
fn kind(&self) -> RequestFieldKind {
match *self {
RequestField::Body(_) => RequestFieldKind::Body,
RequestField::Header(_) => RequestFieldKind::Header,
RequestField::NewtypeBody(_) => RequestFieldKind::NewtypeBody,
RequestField::Path(_) => RequestFieldKind::Path,
RequestField::Query(_) => RequestFieldKind::Query,
RequestField::Body(..) => RequestFieldKind::Body,
RequestField::Header(..) => RequestFieldKind::Header,
RequestField::NewtypeBody(..) => RequestFieldKind::NewtypeBody,
RequestField::Path(..) => RequestFieldKind::Path,
RequestField::Query(..) => RequestFieldKind::Query,
}
}
@ -298,6 +331,10 @@ impl RequestField {
self.kind() == RequestFieldKind::Body
}
fn is_header(&self) -> bool {
self.kind() == RequestFieldKind::Header
}
fn is_path(&self) -> bool {
self.kind() == RequestFieldKind::Path
}
@ -309,7 +346,7 @@ impl RequestField {
fn field(&self) -> &Field {
match *self {
RequestField::Body(ref field) => field,
RequestField::Header(ref field) => field,
RequestField::Header(ref field, _) => field,
RequestField::NewtypeBody(ref field) => field,
RequestField::Path(ref field) => field,
RequestField::Query(ref field) => field,

View File

@ -1,6 +1,6 @@
use quote::{ToTokens, Tokens};
use syn::spanned::Spanned;
use syn::{Field, Meta, NestedMeta};
use syn::{Field, Ident, Lit, Meta, NestedMeta};
use api::strip_serde_attrs;
@ -34,13 +34,13 @@ impl Response {
#field_name: response_body.#field_name,
});
}
ResponseField::Header(ref field) => {
ResponseField::Header(ref field, ref header) => {
let field_name = field.ident.expect("expected field to have an identifier");
let field_type = &field.ty;
let header_name = Ident::from(header.as_ref());
let span = field.span();
tokens.append_all(quote_spanned! {span=>
#field_name: headers.remove::<#field_type>()
#field_name: headers.remove(::http::header::#header_name)
.expect("missing expected request header"),
});
}
@ -80,6 +80,7 @@ impl From<Vec<Field>> for Response {
let fields = fields.into_iter().map(|mut field| {
let mut field_kind = ResponseFieldKind::Body;
let mut header = None;
field.attrs = field.attrs.into_iter().filter(|attr| {
let meta = attr.interpret_meta()
@ -109,7 +110,14 @@ impl From<Vec<Field>> for Response {
}
Meta::NameValue(name_value) => {
match name_value.ident.as_ref() {
"header" => field_kind = ResponseFieldKind::Header,
"header" => {
match name_value.lit {
Lit::Str(lit_str) => header = Some(lit_str.value()),
_ => panic!("ruma_api! header attribute's value must be a string literal"),
}
field_kind = ResponseFieldKind::Header;
}
_ => panic!("ruma_api! name/value pair attribute on requests must be: header"),
}
}
@ -133,7 +141,7 @@ impl From<Vec<Field>> for Response {
return ResponseField::Body(field);
}
}
ResponseFieldKind::Header => ResponseField::Header(field),
ResponseFieldKind::Header => ResponseField::Header(field, header.expect("missing header name")),
ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field),
}
}).collect();
@ -220,7 +228,7 @@ impl ToTokens for Response {
pub enum ResponseField {
Body(Field),
Header(Field),
Header(Field, String),
NewtypeBody(Field),
}
@ -228,21 +236,21 @@ impl ResponseField {
fn field(&self) -> &Field {
match *self {
ResponseField::Body(ref field) => field,
ResponseField::Header(ref field) => field,
ResponseField::Header(ref field, _) => field,
ResponseField::NewtypeBody(ref field) => field,
}
}
fn is_body(&self) -> bool {
match *self {
ResponseField::Body(_) => true,
ResponseField::Body(..) => true,
_ => false,
}
}
fn is_header(&self) -> bool {
match *self {
ResponseField::Header(_) => true,
ResponseField::Header(..) => true,
_ => false,
}
}

View File

@ -1,4 +1,4 @@
#![feature(associated_consts, proc_macro, try_from)]
#![feature(proc_macro, try_from)]
extern crate futures;
extern crate http;