macros: Add request attribute macro
This commit is contained in:
parent
c021a14b60
commit
f22857e682
@ -194,6 +194,44 @@ use crate::UserId;
|
|||||||
/// ```
|
/// ```
|
||||||
pub use ruma_macros::ruma_api;
|
pub use ruma_macros::ruma_api;
|
||||||
|
|
||||||
|
/// Generates [`OutgoingRequest`] and [`IncomingRequest`] implementations.
|
||||||
|
///
|
||||||
|
/// The `OutgoingRequest` impl is be on the `Request` type this attribute is used on. It is
|
||||||
|
/// feature-gated behind `cfg(feature = "client")`.
|
||||||
|
///
|
||||||
|
/// The `IncomingRequest` impl is be on `IncomingRequest`, which is either a type alias to
|
||||||
|
/// `Request` or a fully-owned version of the same, depending of whether `Request` has any
|
||||||
|
/// lifetime parameters.
|
||||||
|
///
|
||||||
|
/// The generated code expects a `METADATA` constant of type [`Metadata`] to be in scope,
|
||||||
|
/// alongside a `Response` type that implements both [`OutgoingResponse`] and
|
||||||
|
/// [`IncomingResponse`].
|
||||||
|
///
|
||||||
|
/// ## Attributes
|
||||||
|
///
|
||||||
|
/// To declare which part of the request a field belongs to:
|
||||||
|
///
|
||||||
|
/// * `#[ruma_api(header = HEADER_NAME)]`: Fields with this attribute will be treated as HTTP
|
||||||
|
/// headers on the request. The value must implement `AsRef<str>`. Generally this is a
|
||||||
|
/// `String`. The attribute value shown above as `HEADER_NAME` must be a `const` expression
|
||||||
|
/// of the type `http::header::HeaderName`, like one of the constants from `http::header`,
|
||||||
|
/// e.g. `CONTENT_TYPE`.
|
||||||
|
/// * `#[ruma_api(path)]`: Fields with this attribute will be inserted into the matching path
|
||||||
|
/// component of the request URL.
|
||||||
|
/// * `#[ruma_api(query)]`: Fields with this attribute will be inserting into the URL's query
|
||||||
|
/// string.
|
||||||
|
/// * `#[ruma_api(query_map)]`: Instead of individual query fields, one query_map field, of any
|
||||||
|
/// type that implements `IntoIterator<Item = (String, String)>` (e.g. `HashMap<String,
|
||||||
|
/// String>`, can be used for cases where an endpoint supports arbitrary query parameters.
|
||||||
|
/// * `#[ruma_api(body)]`: Use this if multiple endpoints should share a request body type, or
|
||||||
|
/// the request body is better expressed as an `enum` rather than a `struct`. The value of
|
||||||
|
/// the field will be used as the JSON body (rather than being a field in the request body
|
||||||
|
/// object).
|
||||||
|
/// * `#[ruma_api(raw_body)]`: Like `body` in that the field annotated with it represents the
|
||||||
|
/// entire request body, but this attribute is for endpoints where the body can be anything,
|
||||||
|
/// not just JSON. The field type must be `Vec<u8>`.
|
||||||
|
pub use ruma_macros::request;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
|
|
||||||
|
@ -89,16 +89,7 @@ impl Request {
|
|||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[doc = #docs]
|
#[doc = #docs]
|
||||||
#[derive(
|
#[#ruma_macros::request(error = #error_ty)]
|
||||||
Clone,
|
|
||||||
Debug,
|
|
||||||
#ruma_macros::Request,
|
|
||||||
#ruma_common::serde::Incoming,
|
|
||||||
#ruma_common::serde::_FakeDeriveSerde,
|
|
||||||
)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
#[incoming_derive(!Deserialize, #ruma_macros::_FakeDeriveRumaApi)]
|
|
||||||
#[ruma_api(error = #error_ty)]
|
|
||||||
#( #struct_attributes )*
|
#( #struct_attributes )*
|
||||||
pub struct #request_ident < #(#lifetimes),* > {
|
pub struct #request_ident < #(#lifetimes),* > {
|
||||||
#fields
|
#fields
|
||||||
|
@ -5,7 +5,7 @@ use quote::{quote, ToTokens};
|
|||||||
use syn::{
|
use syn::{
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
DeriveInput, Field, Generics, Ident, Lifetime, Token, Type,
|
DeriveInput, Field, Generics, Ident, ItemStruct, Lifetime, Token, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -17,6 +17,38 @@ use crate::util::import_ruma_common;
|
|||||||
mod incoming;
|
mod incoming;
|
||||||
mod outgoing;
|
mod outgoing;
|
||||||
|
|
||||||
|
pub fn expand_request(attr: RequestAttr, item: ItemStruct) -> TokenStream {
|
||||||
|
let ruma_common = import_ruma_common();
|
||||||
|
let ruma_macros = quote! { #ruma_common::exports::ruma_macros };
|
||||||
|
|
||||||
|
let error_ty = attr.0.first().map_or_else(
|
||||||
|
|| quote! { #ruma_common::api::error::MatrixError },
|
||||||
|
|DeriveRequestMeta::Error(ty)| quote! { #ty },
|
||||||
|
);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
#ruma_macros::Request,
|
||||||
|
#ruma_common::serde::Incoming,
|
||||||
|
#ruma_common::serde::_FakeDeriveSerde,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
#[incoming_derive(!Deserialize, #ruma_macros::_FakeDeriveRumaApi)]
|
||||||
|
#[ruma_api(error = #error_ty)]
|
||||||
|
#item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RequestAttr(Punctuated<DeriveRequestMeta, Token![,]>);
|
||||||
|
|
||||||
|
impl Parse for RequestAttr {
|
||||||
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
|
Punctuated::<DeriveRequestMeta, Token![,]>::parse_terminated(input).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand_derive_request(input: DeriveInput) -> syn::Result<TokenStream> {
|
pub fn expand_derive_request(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||||
let fields = match input.data {
|
let fields = match input.data {
|
||||||
syn::Data::Struct(s) => s.fields,
|
syn::Data::Struct(s) => s.fields,
|
||||||
|
@ -25,7 +25,11 @@ mod serde;
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
api::{request::expand_derive_request, response::expand_derive_response, Api},
|
api::{
|
||||||
|
request::{expand_derive_request, expand_request},
|
||||||
|
response::expand_derive_response,
|
||||||
|
Api,
|
||||||
|
},
|
||||||
events::{
|
events::{
|
||||||
event::expand_event,
|
event::expand_event,
|
||||||
event_content::expand_event_content,
|
event_content::expand_event_content,
|
||||||
@ -384,6 +388,15 @@ pub fn ruma_api(input: TokenStream) -> TokenStream {
|
|||||||
api.expand_all().into()
|
api.expand_all().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// > ⚠ If this is the only documentation you see, please navigate to the docs for
|
||||||
|
/// > `ruma_common::api::request`, where actual documentation can be found.
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn request(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let attr = parse_macro_input!(attr);
|
||||||
|
let item = parse_macro_input!(item);
|
||||||
|
expand_request(attr, item).into()
|
||||||
|
}
|
||||||
|
|
||||||
/// Internal helper taking care of the request-specific parts of `ruma_api!`.
|
/// Internal helper taking care of the request-specific parts of `ruma_api!`.
|
||||||
#[proc_macro_derive(Request, attributes(ruma_api))]
|
#[proc_macro_derive(Request, attributes(ruma_api))]
|
||||||
pub fn derive_request(input: TokenStream) -> TokenStream {
|
pub fn derive_request(input: TokenStream) -> TokenStream {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user