Use custom synom parsing.
This commit is contained in:
parent
69522626ff
commit
bf7189048a
12
Cargo.toml
12
Cargo.toml
@ -5,14 +5,18 @@ version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
quote = "0.3.15"
|
||||
syn = { version = "0.11.11", features = ["full"] }
|
||||
|
||||
[dependencies.ruma-api]
|
||||
path = "../ruma-api"
|
||||
synom = "0.11.3"
|
||||
|
||||
[dependencies.hyper]
|
||||
git = "https://github.com/hyperium/hyper"
|
||||
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
|
||||
|
||||
[dependencies.ruma-api]
|
||||
path = "../ruma-api"
|
||||
|
||||
[dependencies.syn]
|
||||
features = ["full"]
|
||||
version = "0.11.11"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
168
src/lib.rs
168
src/lib.rs
@ -1,25 +1,27 @@
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate hyper;
|
||||
extern crate proc_macro;
|
||||
extern crate quote;
|
||||
extern crate ruma_api;
|
||||
extern crate syn;
|
||||
#[macro_use] extern crate synom;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use hyper::Method;
|
||||
use quote::{ToTokens, Tokens};
|
||||
use syn::{ExprKind, Item, ItemKind, Lit, parse_items};
|
||||
use quote::Tokens;
|
||||
use syn::{Expr, Field, Ident, Item};
|
||||
|
||||
use parse::{Entry, parse_entries};
|
||||
|
||||
mod parse;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn ruma_api(input: TokenStream) -> TokenStream {
|
||||
let items = parse_items(&input.to_string())
|
||||
.expect("failed to parse input");
|
||||
let entries = parse_entries(&input.to_string()).expect("ruma_api! failed to parse input");
|
||||
|
||||
let api = Api::from(items);
|
||||
let api = Api::from(entries);
|
||||
|
||||
api.output().parse().expect("failed to parse output")
|
||||
api.output().parse().expect("ruma_api! failed to parse output as a TokenStream")
|
||||
}
|
||||
|
||||
struct Api {
|
||||
@ -34,41 +36,19 @@ impl Api {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Item>> for Api {
|
||||
fn from(items: Vec<Item>) -> Api {
|
||||
if items.len() != 3 {
|
||||
panic!("ruma_api! expects exactly three items: const METADATA, struct Request, and struct Response");
|
||||
}
|
||||
|
||||
let mut metadata = None;
|
||||
let mut request = None;
|
||||
let mut response = None;
|
||||
|
||||
for item in items {
|
||||
match &item.ident.to_string()[..] {
|
||||
"METADATA" => metadata = Some(Metadata::from(item)),
|
||||
"Request" => request = Some(Request::from(item)),
|
||||
"Response" => response = Some(Response::from(item)),
|
||||
other => panic!("ruma_api! found unexpected item: {}", other),
|
||||
}
|
||||
}
|
||||
|
||||
if metadata.is_none() {
|
||||
panic!("ruma_api! requires item: const METADATA");
|
||||
}
|
||||
|
||||
if request.is_none() {
|
||||
panic!("ruma_api! requires item: struct Request");
|
||||
}
|
||||
|
||||
if response.is_none() {
|
||||
panic!("ruma_api! requires item: struct Response");
|
||||
}
|
||||
|
||||
impl From<Vec<Entry>> for Api {
|
||||
fn from(entries: Vec<Entry>) -> Api {
|
||||
Api {
|
||||
metadata: metadata.expect("metadata is missing"),
|
||||
request: request.expect("request is missing"),
|
||||
response: response.expect("response is missing"),
|
||||
metadata: Metadata {
|
||||
description: Tokens::new(),
|
||||
method: Tokens::new(),
|
||||
name: Tokens::new(),
|
||||
path: Tokens::new(),
|
||||
rate_limited: Tokens::new(),
|
||||
requires_authentication: Tokens::new(),
|
||||
},
|
||||
request: Request,
|
||||
response: Response,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,104 +62,6 @@ struct Metadata {
|
||||
requires_authentication: Tokens,
|
||||
}
|
||||
|
||||
impl From<Item> for Metadata {
|
||||
fn from(item: Item) -> Self {
|
||||
let expr = match item.node {
|
||||
ItemKind::Const(_, expr) => expr,
|
||||
_ => panic!("ruma_api! expects METADATA to be a const item"),
|
||||
};
|
||||
|
||||
let field_values = match expr.node {
|
||||
ExprKind::Struct(_, field_values, _) => field_values,
|
||||
_ => panic!("ruma_api! expects METADATA to be a Metadata struct"),
|
||||
};
|
||||
|
||||
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_value in field_values {
|
||||
match &field_value.ident.to_string()[..] {
|
||||
"description" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Lit(Lit::Str(value, _)) => description = Some(tokens_for(value)),
|
||||
_ => panic!("ruma_api! expects metadata description to be a &'static str"),
|
||||
}
|
||||
}
|
||||
"method" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Path(_, value) => method = Some(tokens_for(value)),
|
||||
_ => panic!("ruma_api! expects metadata method to be a path"),
|
||||
}
|
||||
}
|
||||
"name" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Lit(Lit::Str(value, _)) => name = Some(tokens_for(value)),
|
||||
_ => panic!("ruma_api! expects metadata name to be a &'static str"),
|
||||
}
|
||||
}
|
||||
"path" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Lit(Lit::Str(value, _)) => path = Some(tokens_for(value)),
|
||||
_ => panic!("ruma_api! expects metadata path to be a &'static str"),
|
||||
}
|
||||
}
|
||||
"rate_limited" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Lit(Lit::Bool(value)) => rate_limited = Some(tokens_for(value)),
|
||||
_ => panic!("ruma_api! expects metadata rate_limited to be a bool"),
|
||||
}
|
||||
}
|
||||
"requires_authentication" => {
|
||||
match field_value.expr.node {
|
||||
ExprKind::Lit(Lit::Bool(value)) => {
|
||||
requires_authentication = Some(tokens_for(value));
|
||||
}
|
||||
_ => panic!("ruma_api! expects metadata requires_authentication to be a bool"),
|
||||
}
|
||||
}
|
||||
other => panic!("ruma_api! found unexpected metadata field: {}", other),
|
||||
}
|
||||
}
|
||||
|
||||
if description.is_none() {
|
||||
panic!("ruma_api! metadata requires field: description");
|
||||
}
|
||||
|
||||
if method.is_none() {
|
||||
panic!("ruma_api! metadata requires field: method");
|
||||
}
|
||||
|
||||
if name.is_none() {
|
||||
panic!("ruma_api! metadata requires field: name");
|
||||
}
|
||||
|
||||
if path.is_none() {
|
||||
panic!("ruma_api! metadata requires field: path");
|
||||
}
|
||||
|
||||
if rate_limited.is_none() {
|
||||
panic!("ruma_api! metadata requires field: rate_limited");
|
||||
}
|
||||
|
||||
if requires_authentication.is_none() {
|
||||
panic!("ruma_api! metadata requires field: requires_authentication");
|
||||
}
|
||||
|
||||
Metadata {
|
||||
description: description.expect("description is missing"),
|
||||
method: method.expect("method is missing"),
|
||||
name: name.expect("name is missing"),
|
||||
path: path.expect("path is missing"),
|
||||
rate_limited: rate_limited.expect("rate limited is missing"),
|
||||
requires_authentication: requires_authentication.expect("requires_authentication is missing"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Request;
|
||||
|
||||
impl From<Item> for Request {
|
||||
@ -197,9 +79,3 @@ impl From<Item> for Response {
|
||||
// panic!("ruma_api! could not parse Response");
|
||||
}
|
||||
}
|
||||
|
||||
fn tokens_for<T>(value: T) -> Tokens where T: ToTokens {
|
||||
let mut tokens = Tokens::new();
|
||||
value.to_tokens(&mut tokens);
|
||||
tokens
|
||||
}
|
||||
|
144
src/parse.rs
Normal file
144
src/parse.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use syn::{
|
||||
Attribute,
|
||||
AttrStyle,
|
||||
Expr,
|
||||
Field,
|
||||
Ident,
|
||||
Item,
|
||||
Lit,
|
||||
MetaItem,
|
||||
NestedMetaItem,
|
||||
StrStyle,
|
||||
Token,
|
||||
TokenTree,
|
||||
Visibility,
|
||||
};
|
||||
use syn::parse::{expr, ident, lit, ty};
|
||||
use synom::space::{block_comment, whitespace};
|
||||
|
||||
pub enum Entry {
|
||||
Metadata(Vec<(Ident, Expr)>),
|
||||
Request(Vec<Field>),
|
||||
Response(Vec<Field>),
|
||||
}
|
||||
|
||||
named!(pub parse_entries -> Vec<Entry>, do_parse!(
|
||||
entries: many0!(entry) >>
|
||||
(entries)
|
||||
));
|
||||
|
||||
named!(entry -> Entry, alt!(
|
||||
do_parse!(
|
||||
keyword!("metadata") >>
|
||||
punct!(":") >>
|
||||
punct!("{") >>
|
||||
fields: many0!(struct_init_field) >>
|
||||
(Entry::Metadata(fields))
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
keyword!("request") >>
|
||||
punct!(":") >>
|
||||
punct!("{") >>
|
||||
fields: many0!(struct_field) >>
|
||||
(Entry::Request(fields))
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
keyword!("response") >>
|
||||
punct!(":") >>
|
||||
punct!("{") >>
|
||||
fields: many0!(struct_field) >>
|
||||
(Entry::Response(fields))
|
||||
)
|
||||
));
|
||||
|
||||
// Everything below copy/pasted from syn 0.11.11.
|
||||
|
||||
named!(struct_init_field -> (Ident, Expr), do_parse!(
|
||||
ident: ident >>
|
||||
punct!(":") >>
|
||||
expr: expr >>
|
||||
punct!(",") >>
|
||||
(ident, expr)
|
||||
));
|
||||
|
||||
named!(struct_field -> Field, do_parse!(
|
||||
attrs: many0!(outer_attr) >>
|
||||
id: ident >>
|
||||
punct!(":") >>
|
||||
ty: ty >>
|
||||
(Field {
|
||||
ident: Some(id),
|
||||
vis: Visibility::Public,
|
||||
attrs: attrs,
|
||||
ty: ty,
|
||||
})
|
||||
));
|
||||
|
||||
named!(outer_attr -> Attribute, alt!(
|
||||
do_parse!(
|
||||
punct!("#") >>
|
||||
punct!("[") >>
|
||||
meta_item: meta_item >>
|
||||
punct!("]") >>
|
||||
(Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: meta_item,
|
||||
is_sugared_doc: false,
|
||||
})
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
punct!("///") >>
|
||||
not!(tag!("/")) >>
|
||||
content: take_until!("\n") >>
|
||||
(Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::NameValue(
|
||||
"doc".into(),
|
||||
format!("///{}", content).into(),
|
||||
),
|
||||
is_sugared_doc: true,
|
||||
})
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
option!(whitespace) >>
|
||||
peek!(tuple!(tag!("/**"), not!(tag!("*")))) >>
|
||||
com: block_comment >>
|
||||
(Attribute {
|
||||
style: AttrStyle::Outer,
|
||||
value: MetaItem::NameValue(
|
||||
"doc".into(),
|
||||
com.into(),
|
||||
),
|
||||
is_sugared_doc: true,
|
||||
})
|
||||
)
|
||||
));
|
||||
|
||||
named!(meta_item -> MetaItem, alt!(
|
||||
do_parse!(
|
||||
id: ident >>
|
||||
punct!("(") >>
|
||||
inner: terminated_list!(punct!(","), nested_meta_item) >>
|
||||
punct!(")") >>
|
||||
(MetaItem::List(id, inner))
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
name: ident >>
|
||||
punct!("=") >>
|
||||
value: lit >>
|
||||
(MetaItem::NameValue(name, value))
|
||||
)
|
||||
|
|
||||
map!(ident, MetaItem::Word)
|
||||
));
|
||||
|
||||
named!(nested_meta_item -> NestedMetaItem, alt!(
|
||||
meta_item => { NestedMetaItem::MetaItem }
|
||||
|
|
||||
lit => { NestedMetaItem::Literal }
|
||||
));
|
@ -8,18 +8,16 @@ pub mod get_supported_versions {
|
||||
use ruma_api_macros::ruma_api;
|
||||
|
||||
ruma_api! {
|
||||
const METADATA: Metadata = Metadata {
|
||||
metadata: {
|
||||
description: "Get the versions of the client-server API supported by this homeserver.",
|
||||
method: Method::Get,
|
||||
name: "api_versions",
|
||||
path: "/_matrix/client/versions",
|
||||
rate_limited: false,
|
||||
requires_authentication: true,
|
||||
};
|
||||
|
||||
struct Request;
|
||||
|
||||
struct Response {
|
||||
},
|
||||
request: {},
|
||||
response: {
|
||||
/// A list of Matrix client API protocol versions supported by the homeserver.
|
||||
pub versions: Vec<String>,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user