Use three block form for the macro, fix some bugs, construct metadata tokens.

This commit is contained in:
Jimmy Cuadra 2017-05-13 01:16:44 -07:00
parent bf7189048a
commit d0a35341a2
3 changed files with 101 additions and 27 deletions

View File

@ -8,7 +8,7 @@ extern crate syn;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::Tokens; use quote::{ToTokens, Tokens};
use syn::{Expr, Field, Ident, Item}; use syn::{Expr, Field, Ident, Item};
use parse::{Entry, parse_entries}; use parse::{Entry, parse_entries};
@ -38,17 +38,26 @@ impl Api {
impl From<Vec<Entry>> for Api { impl From<Vec<Entry>> for Api {
fn from(entries: Vec<Entry>) -> Api { fn from(entries: Vec<Entry>) -> Api {
if entries.len() != 3 {
panic!("ruma_api! expects 3 blocks: metadata, request, and response");
}
let mut metadata = None;
let mut request = None;
let mut response = None;
for entry in entries {
match entry {
Entry::Metadata(fields) => metadata = Some(Metadata::from(fields)),
Entry::Request(fields) => request = Some(Request::from(fields)),
Entry::Response(fields) => response = Some(Response::from(fields)),
}
}
Api { Api {
metadata: Metadata { metadata: metadata.expect("ruma_api! is missing metadata"),
description: Tokens::new(), request: request.expect("ruma_api! is missing request"),
method: Tokens::new(), response: response.expect("ruma_api! is missing response"),
name: Tokens::new(),
path: Tokens::new(),
rate_limited: Tokens::new(),
requires_authentication: Tokens::new(),
},
request: Request,
response: Response,
} }
} }
} }
@ -62,20 +71,68 @@ struct Metadata {
requires_authentication: Tokens, requires_authentication: Tokens,
} }
impl From<Vec<(Ident, Expr)>> for Metadata {
fn from(fields: Vec<(Ident, Expr)>) -> Self {
let mut description = None;
let mut method = None;
let mut name = None;
let mut path = None;
let mut rate_limited = None;
let mut requires_authentication = None;
for field in fields {
let (identifier, expression) = field;
if identifier == Ident::new("description") {
description = Some(tokens_for(expression));
} else if identifier == Ident::new("method") {
method = Some(tokens_for(expression));
} else if identifier == Ident::new("name") {
name = Some(tokens_for(expression));
} else if identifier == Ident::new("path") {
path = Some(tokens_for(expression));
} else if identifier == Ident::new("rate_limited") {
rate_limited = Some(tokens_for(expression));
} else if identifier == Ident::new("requires_authentication") {
requires_authentication = Some(tokens_for(expression));
} else {
panic!("ruma_api! metadata included unexpected field: {}", identifier);
}
}
Metadata {
description: description.expect("ruma_api! metadata is missing description"),
method: method.expect("ruma_api! metadata is missing method"),
name: name.expect("ruma_api! metadata is missing name"),
path: path.expect("ruma_api! metadata is missing path"),
rate_limited: rate_limited.expect("ruma_api! metadata is missing rate_limited"),
requires_authentication: requires_authentication
.expect("ruma_api! metadata is missing requires_authentication"),
}
}
}
struct Request; struct Request;
impl From<Item> for Request { impl From<Vec<Field>> for Request {
fn from(item: Item) -> Self { fn from(fields: Vec<Field>) -> Self {
Request Request
// panic!("ruma_api! could not parse Request");
} }
} }
struct Response; struct Response;
impl From<Item> for Response { impl From<Vec<Field>> for Response {
fn from(item: Item) -> Self { fn from(fields: Vec<Field>) -> Self {
Response Response
// panic!("ruma_api! could not parse Response");
} }
} }
/// Helper method for turning a value into tokens.
fn tokens_for<T>(value: T) -> Tokens where T: ToTokens {
let mut tokens = Tokens::new();
value.to_tokens(&mut tokens);
tokens
}

View File

@ -16,6 +16,7 @@ use syn::{
use syn::parse::{expr, ident, lit, ty}; use syn::parse::{expr, ident, lit, ty};
use synom::space::{block_comment, whitespace}; use synom::space::{block_comment, whitespace};
#[derive(Debug)]
pub enum Entry { pub enum Entry {
Metadata(Vec<(Ident, Expr)>), Metadata(Vec<(Ident, Expr)>),
Request(Vec<Field>), Request(Vec<Field>),
@ -30,25 +31,25 @@ named!(pub parse_entries -> Vec<Entry>, do_parse!(
named!(entry -> Entry, alt!( named!(entry -> Entry, alt!(
do_parse!( do_parse!(
keyword!("metadata") >> keyword!("metadata") >>
punct!(":") >>
punct!("{") >> punct!("{") >>
fields: many0!(struct_init_field) >> fields: many0!(struct_init_field) >>
punct!("}") >>
(Entry::Metadata(fields)) (Entry::Metadata(fields))
) )
| |
do_parse!( do_parse!(
keyword!("request") >> keyword!("request") >>
punct!(":") >>
punct!("{") >> punct!("{") >>
fields: many0!(struct_field) >> fields: terminated_list!(punct!(","), struct_field) >>
punct!("}") >>
(Entry::Request(fields)) (Entry::Request(fields))
) )
| |
do_parse!( do_parse!(
keyword!("response") >> keyword!("response") >>
punct!(":") >>
punct!("{") >> punct!("{") >>
fields: many0!(struct_field) >> fields: terminated_list!(punct!(","), struct_field) >>
punct!("}") >>
(Entry::Response(fields)) (Entry::Response(fields))
) )
)); ));
@ -63,14 +64,22 @@ named!(struct_init_field -> (Ident, Expr), do_parse!(
(ident, expr) (ident, expr)
)); ));
named!(pub struct_like_body -> Vec<Field>, do_parse!(
punct!("{") >>
fields: terminated_list!(punct!(","), struct_field) >>
punct!("}") >>
(fields)
));
named!(struct_field -> Field, do_parse!( named!(struct_field -> Field, do_parse!(
attrs: many0!(outer_attr) >> attrs: many0!(outer_attr) >>
vis: visibility >>
id: ident >> id: ident >>
punct!(":") >> punct!(":") >>
ty: ty >> ty: ty >>
(Field { (Field {
ident: Some(id), ident: Some(id),
vis: Visibility::Public, vis: Visibility::Public, // Ignore declared visibility, always make fields public
attrs: attrs, attrs: attrs,
ty: ty, ty: ty,
}) })
@ -142,3 +151,9 @@ named!(nested_meta_item -> NestedMetaItem, alt!(
| |
lit => { NestedMetaItem::Literal } lit => { NestedMetaItem::Literal }
)); ));
named!(visibility -> Visibility, alt!(
keyword!("pub") => { |_| Visibility::Public }
|
epsilon!() => { |_| Visibility::Inherited }
));

View File

@ -8,16 +8,18 @@ pub mod get_supported_versions {
use ruma_api_macros::ruma_api; use ruma_api_macros::ruma_api;
ruma_api! { ruma_api! {
metadata: { metadata {
description: "Get the versions of the client-server API supported by this homeserver.", description: "Get the versions of the client-server API supported by this homeserver.",
method: Method::Get, method: Method::Get,
name: "api_versions", name: "api_versions",
path: "/_matrix/client/versions", path: "/_matrix/client/versions",
rate_limited: false, rate_limited: false,
requires_authentication: true, requires_authentication: true,
}, }
request: {},
response: { request {}
response {
/// A list of Matrix client API protocol versions supported by the homeserver. /// A list of Matrix client API protocol versions supported by the homeserver.
pub versions: Vec<String>, pub versions: Vec<String>,
} }