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]
|
[dependencies]
|
||||||
quote = "0.3.15"
|
quote = "0.3.15"
|
||||||
syn = { version = "0.11.11", features = ["full"] }
|
synom = "0.11.3"
|
||||||
|
|
||||||
[dependencies.ruma-api]
|
|
||||||
path = "../ruma-api"
|
|
||||||
|
|
||||||
[dependencies.hyper]
|
[dependencies.hyper]
|
||||||
git = "https://github.com/hyperium/hyper"
|
git = "https://github.com/hyperium/hyper"
|
||||||
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
|
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
|
||||||
|
|
||||||
|
[dependencies.ruma-api]
|
||||||
|
path = "../ruma-api"
|
||||||
|
|
||||||
|
[dependencies.syn]
|
||||||
|
features = ["full"]
|
||||||
|
version = "0.11.11"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
168
src/lib.rs
168
src/lib.rs
@ -1,25 +1,27 @@
|
|||||||
#![feature(proc_macro)]
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
extern crate hyper;
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
extern crate quote;
|
extern crate quote;
|
||||||
extern crate ruma_api;
|
extern crate ruma_api;
|
||||||
extern crate syn;
|
extern crate syn;
|
||||||
|
#[macro_use] extern crate synom;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
use hyper::Method;
|
use quote::Tokens;
|
||||||
use quote::{ToTokens, Tokens};
|
use syn::{Expr, Field, Ident, Item};
|
||||||
use syn::{ExprKind, Item, ItemKind, Lit, parse_items};
|
|
||||||
|
use parse::{Entry, parse_entries};
|
||||||
|
|
||||||
|
mod parse;
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn ruma_api(input: TokenStream) -> TokenStream {
|
pub fn ruma_api(input: TokenStream) -> TokenStream {
|
||||||
let items = parse_items(&input.to_string())
|
let entries = parse_entries(&input.to_string()).expect("ruma_api! failed to parse input");
|
||||||
.expect("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 {
|
struct Api {
|
||||||
@ -34,41 +36,19 @@ impl Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<Item>> for Api {
|
impl From<Vec<Entry>> for Api {
|
||||||
fn from(items: Vec<Item>) -> Api {
|
fn from(entries: Vec<Entry>) -> 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
Api {
|
Api {
|
||||||
metadata: metadata.expect("metadata is missing"),
|
metadata: Metadata {
|
||||||
request: request.expect("request is missing"),
|
description: Tokens::new(),
|
||||||
response: response.expect("response is missing"),
|
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,
|
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;
|
struct Request;
|
||||||
|
|
||||||
impl From<Item> for Request {
|
impl From<Item> for Request {
|
||||||
@ -197,9 +79,3 @@ impl From<Item> for Response {
|
|||||||
// panic!("ruma_api! could not parse 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;
|
use ruma_api_macros::ruma_api;
|
||||||
|
|
||||||
ruma_api! {
|
ruma_api! {
|
||||||
const METADATA: Metadata = 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: {},
|
||||||
struct Request;
|
response: {
|
||||||
|
|
||||||
struct 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>,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user