1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
braced,
parse::{Parse, ParseStream},
Attribute, Field, Token, Type,
};
use self::{api_metadata::Metadata, api_request::Request, api_response::Response};
use crate::util::import_ruma_common;
mod api_metadata;
mod api_request;
mod api_response;
mod attribute;
mod auth_scheme;
pub mod request;
pub mod response;
mod util;
mod version;
mod kw {
use syn::custom_keyword;
custom_keyword!(error);
custom_keyword!(request);
custom_keyword!(response);
}
pub struct Api {
metadata: Metadata,
request: Option<Request>,
response: Option<Response>,
error_ty: Option<Type>,
}
impl Api {
pub fn expand_all(self) -> TokenStream {
let ruma_common = import_ruma_common();
let http = quote! { #ruma_common::exports::http };
let metadata = &self.metadata;
let description = &metadata.description;
let method = &metadata.method;
let name = &metadata.name;
let unstable_path = util::map_option_literal(&metadata.unstable_path);
let r0_path = util::map_option_literal(&metadata.r0_path);
let stable_path = util::map_option_literal(&metadata.stable_path);
let rate_limited = &self.metadata.rate_limited;
let authentication = &self.metadata.authentication;
let added = util::map_option_literal(&metadata.added);
let deprecated = util::map_option_literal(&metadata.deprecated);
let removed = util::map_option_literal(&metadata.removed);
let error_ty = self.error_ty.map_or_else(
|| quote! { #ruma_common::api::error::MatrixError },
|err_ty| quote! { #err_ty },
);
let request = self.request.map(|req| req.expand(metadata, &error_ty, &ruma_common));
let response = self.response.map(|res| res.expand(metadata, &error_ty, &ruma_common));
let metadata_doc = format!("Metadata for the `{}` API endpoint.", name.value());
quote! {
#[doc = #metadata_doc]
pub const METADATA: #ruma_common::api::Metadata = #ruma_common::api::Metadata {
description: #description,
method: #http::Method::#method,
name: #name,
unstable_path: #unstable_path,
r0_path: #r0_path,
stable_path: #stable_path,
added: #added,
deprecated: #deprecated,
removed: #removed,
rate_limited: #rate_limited,
authentication: #ruma_common::api::AuthScheme::#authentication,
};
#request
#response
#[cfg(not(any(feature = "client", feature = "server")))]
type _SilenceUnusedError = #error_ty;
}
}
}
impl Parse for Api {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let metadata: Metadata = input.parse()?;
let req_attrs = input.call(Attribute::parse_outer)?;
let (request, attributes) = if input.peek(kw::request) {
let request = parse_request(input, req_attrs)?;
let after_req_attrs = input.call(Attribute::parse_outer)?;
(Some(request), after_req_attrs)
} else {
(None, req_attrs)
};
let response = if input.peek(kw::response) {
Some(parse_response(input, attributes)?)
} else if !attributes.is_empty() {
return Err(syn::Error::new_spanned(
&attributes[0],
"attributes are not supported on the error type",
));
} else {
None
};
let error_ty = input
.peek(kw::error)
.then(|| {
let _: kw::error = input.parse()?;
let _: Token![:] = input.parse()?;
input.parse()
})
.transpose()?;
Ok(Self { metadata, request, response, error_ty })
}
}
fn parse_request(input: ParseStream<'_>, attributes: Vec<Attribute>) -> syn::Result<Request> {
let request_kw: kw::request = input.parse()?;
let _: Token![:] = input.parse()?;
let fields;
braced!(fields in input);
let fields = fields.parse_terminated::<_, Token![,]>(Field::parse_named)?;
Ok(Request { request_kw, attributes, fields })
}
fn parse_response(input: ParseStream<'_>, attributes: Vec<Attribute>) -> syn::Result<Response> {
let response_kw: kw::response = input.parse()?;
let _: Token![:] = input.parse()?;
let fields;
braced!(fields in input);
let fields = fields.parse_terminated::<_, Token![,]>(Field::parse_named)?;
Ok(Response { attributes, fields, response_kw })
}