WIP
This commit is contained in:
commit
69522626ff
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Jimmy Cuadra <jimmy@jimmycuadra.com>"]
|
||||||
|
name = "ruma-api-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.3.15"
|
||||||
|
syn = { version = "0.11.11", features = ["full"] }
|
||||||
|
|
||||||
|
[dependencies.ruma-api]
|
||||||
|
path = "../ruma-api"
|
||||||
|
|
||||||
|
[dependencies.hyper]
|
||||||
|
git = "https://github.com/hyperium/hyper"
|
||||||
|
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
205
src/lib.rs
Normal file
205
src/lib.rs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate hyper;
|
||||||
|
extern crate proc_macro;
|
||||||
|
extern crate quote;
|
||||||
|
extern crate ruma_api;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
use hyper::Method;
|
||||||
|
use quote::{ToTokens, Tokens};
|
||||||
|
use syn::{ExprKind, Item, ItemKind, Lit, parse_items};
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn ruma_api(input: TokenStream) -> TokenStream {
|
||||||
|
let items = parse_items(&input.to_string())
|
||||||
|
.expect("failed to parse input");
|
||||||
|
|
||||||
|
let api = Api::from(items);
|
||||||
|
|
||||||
|
api.output().parse().expect("failed to parse output")
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Api {
|
||||||
|
metadata: Metadata,
|
||||||
|
request: Request,
|
||||||
|
response: Response,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Api {
|
||||||
|
fn output(&self) -> Tokens {
|
||||||
|
Tokens::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
Api {
|
||||||
|
metadata: metadata.expect("metadata is missing"),
|
||||||
|
request: request.expect("request is missing"),
|
||||||
|
response: response.expect("response is missing"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Metadata {
|
||||||
|
description: Tokens,
|
||||||
|
method: Tokens,
|
||||||
|
name: Tokens,
|
||||||
|
path: Tokens,
|
||||||
|
rate_limited: 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;
|
||||||
|
|
||||||
|
impl From<Item> for Request {
|
||||||
|
fn from(item: Item) -> Self {
|
||||||
|
Request
|
||||||
|
// panic!("ruma_api! could not parse Request");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Response;
|
||||||
|
|
||||||
|
impl From<Item> for Response {
|
||||||
|
fn from(item: Item) -> Self {
|
||||||
|
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
|
||||||
|
}
|
27
tests/ruma_api_macros.rs
Normal file
27
tests/ruma_api_macros.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate hyper;
|
||||||
|
extern crate ruma_api;
|
||||||
|
extern crate ruma_api_macros;
|
||||||
|
|
||||||
|
pub mod get_supported_versions {
|
||||||
|
use ruma_api_macros::ruma_api;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
const 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 {
|
||||||
|
/// 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