Add query_map attribute to ruma_api
This commit is contained in:
parent
71372ae910
commit
f0e724d391
@ -135,7 +135,41 @@ impl ToTokens for Api {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let set_request_query = if self.request.has_query_fields() {
|
let set_request_query = if let Some(field) = self.request.query_map_field() {
|
||||||
|
let field_name = field.ident.as_ref().expect("expected field to have identifier");
|
||||||
|
let field_type = &field.ty;
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
// This function exists so that the compiler will throw an
|
||||||
|
// error when the type of the field with the query_map
|
||||||
|
// attribute doesn't implement IntoIterator<Item = (String, String)>
|
||||||
|
//
|
||||||
|
// This is necessary because the serde_urlencoded::to_string
|
||||||
|
// call will result in a runtime error when the type cannot be
|
||||||
|
// encoded as a list key-value pairs (?key1=value1&key2=value2)
|
||||||
|
//
|
||||||
|
// By asserting that it implements the iterator trait, we can
|
||||||
|
// ensure that it won't fail.
|
||||||
|
fn assert_trait_impl<T>()
|
||||||
|
where
|
||||||
|
T: std::iter::IntoIterator<Item = (std::string::String, std::string::String)>,
|
||||||
|
{}
|
||||||
|
assert_trait_impl::<#field_type>();
|
||||||
|
|
||||||
|
let request_query = RequestQuery(request.#field_name);
|
||||||
|
let query_str = ruma_api::exports::serde_urlencoded::to_string(
|
||||||
|
request_query,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let query_opt: Option<&str> = if query_str.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(&query_str)
|
||||||
|
};
|
||||||
|
|
||||||
|
url.set_query(query_opt);
|
||||||
|
}
|
||||||
|
} else if self.request.has_query_fields() {
|
||||||
let request_query_init_fields = self.request.request_query_init_fields();
|
let request_query_init_fields = self.request.request_query_init_fields();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -82,6 +82,11 @@ impl Request {
|
|||||||
self.fields.iter().find_map(RequestField::as_newtype_body_field)
|
self.fields.iter().find_map(RequestField::as_newtype_body_field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the query map field.
|
||||||
|
pub fn query_map_field(&self) -> Option<&Field> {
|
||||||
|
self.fields.iter().find_map(RequestField::as_query_map_field)
|
||||||
|
}
|
||||||
|
|
||||||
/// Produces code for a struct initializer for body fields on a variable named `request`.
|
/// Produces code for a struct initializer for body fields on a variable named `request`.
|
||||||
pub fn request_body_init_fields(&self) -> TokenStream {
|
pub fn request_body_init_fields(&self) -> TokenStream {
|
||||||
self.struct_init_fields(RequestFieldKind::Body, quote!(request))
|
self.struct_init_fields(RequestFieldKind::Body, quote!(request))
|
||||||
@ -127,6 +132,7 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
|
|
||||||
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
||||||
let mut newtype_body_field = None;
|
let mut newtype_body_field = None;
|
||||||
|
let mut query_map_field = None;
|
||||||
|
|
||||||
let fields = raw
|
let fields = raw
|
||||||
.fields
|
.fields
|
||||||
@ -172,10 +178,26 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
}
|
}
|
||||||
"path" => RequestFieldKind::Path,
|
"path" => RequestFieldKind::Path,
|
||||||
"query" => RequestFieldKind::Query,
|
"query" => RequestFieldKind::Query,
|
||||||
|
"query_map" => {
|
||||||
|
if let Some(f) = &query_map_field {
|
||||||
|
let mut error = syn::Error::new_spanned(
|
||||||
|
field,
|
||||||
|
"There can only be one query map field",
|
||||||
|
);
|
||||||
|
error.combine(syn::Error::new_spanned(
|
||||||
|
f,
|
||||||
|
"Previous query map field",
|
||||||
|
));
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
query_map_field = Some(field.clone());
|
||||||
|
RequestFieldKind::QueryMap
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new_spanned(
|
return Err(syn::Error::new_spanned(
|
||||||
ident,
|
ident,
|
||||||
"Invalid #[ruma_api] argument, expected one of `body`, `path`, `query`",
|
"Invalid #[ruma_api] argument, expected one of `body`, `path`, `query`, `query_map`",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,6 +232,14 @@ impl TryFrom<RawRequest> for Request {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if query_map_field.is_some() && fields.iter().any(|f| f.is_query()) {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
// TODO: raw,
|
||||||
|
raw.request_kw,
|
||||||
|
"Can't have both a query map field and regular query fields",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Self { fields })
|
Ok(Self { fields })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,7 +305,20 @@ impl ToTokens for Request {
|
|||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let request_query_struct = if self.has_query_fields() {
|
let request_query_struct = if let Some(field) = self.query_map_field() {
|
||||||
|
let ty = &field.ty;
|
||||||
|
let span = field.span();
|
||||||
|
|
||||||
|
quote_spanned! {span=>
|
||||||
|
/// Data in the request's query string.
|
||||||
|
#[derive(
|
||||||
|
Debug,
|
||||||
|
ruma_api::exports::serde::Deserialize,
|
||||||
|
ruma_api::exports::serde::Serialize,
|
||||||
|
)]
|
||||||
|
struct RequestQuery(#ty);
|
||||||
|
}
|
||||||
|
} else if self.has_query_fields() {
|
||||||
let fields = self.fields.iter().filter_map(RequestField::as_query_field);
|
let fields = self.fields.iter().filter_map(RequestField::as_query_field);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
@ -317,6 +360,8 @@ pub enum RequestField {
|
|||||||
Path(Field),
|
Path(Field),
|
||||||
/// Data that appears in the query string.
|
/// Data that appears in the query string.
|
||||||
Query(Field),
|
Query(Field),
|
||||||
|
/// Data that appears in the query string as dynamic key-value pairs.
|
||||||
|
QueryMap(Field),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequestField {
|
impl RequestField {
|
||||||
@ -330,6 +375,7 @@ impl RequestField {
|
|||||||
RequestFieldKind::NewtypeBody => RequestField::NewtypeBody(field),
|
RequestFieldKind::NewtypeBody => RequestField::NewtypeBody(field),
|
||||||
RequestFieldKind::Path => RequestField::Path(field),
|
RequestFieldKind::Path => RequestField::Path(field),
|
||||||
RequestFieldKind::Query => RequestField::Query(field),
|
RequestFieldKind::Query => RequestField::Query(field),
|
||||||
|
RequestFieldKind::QueryMap => RequestField::QueryMap(field),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,6 +387,7 @@ impl RequestField {
|
|||||||
RequestField::NewtypeBody(..) => RequestFieldKind::NewtypeBody,
|
RequestField::NewtypeBody(..) => RequestFieldKind::NewtypeBody,
|
||||||
RequestField::Path(..) => RequestFieldKind::Path,
|
RequestField::Path(..) => RequestFieldKind::Path,
|
||||||
RequestField::Query(..) => RequestFieldKind::Query,
|
RequestField::Query(..) => RequestFieldKind::Query,
|
||||||
|
RequestField::QueryMap(..) => RequestFieldKind::QueryMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,6 +431,11 @@ impl RequestField {
|
|||||||
self.field_of_kind(RequestFieldKind::Query)
|
self.field_of_kind(RequestFieldKind::Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the contained field if this request field is a query map kind.
|
||||||
|
fn as_query_map_field(&self) -> Option<&Field> {
|
||||||
|
self.field_of_kind(RequestFieldKind::QueryMap)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the inner `Field` value.
|
/// Gets the inner `Field` value.
|
||||||
fn field(&self) -> &Field {
|
fn field(&self) -> &Field {
|
||||||
match self {
|
match self {
|
||||||
@ -391,7 +443,8 @@ impl RequestField {
|
|||||||
| RequestField::Header(field, _)
|
| RequestField::Header(field, _)
|
||||||
| RequestField::NewtypeBody(field)
|
| RequestField::NewtypeBody(field)
|
||||||
| RequestField::Path(field)
|
| RequestField::Path(field)
|
||||||
| RequestField::Query(field) => field,
|
| RequestField::Query(field)
|
||||||
|
| RequestField::QueryMap(field) => field,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,4 +471,6 @@ enum RequestFieldKind {
|
|||||||
Path,
|
Path,
|
||||||
/// See the similarly named variant of `RequestField`.
|
/// See the similarly named variant of `RequestField`.
|
||||||
Query,
|
Query,
|
||||||
|
/// See the similarly named variant of `RequestField`.
|
||||||
|
QueryMap,
|
||||||
}
|
}
|
||||||
|
@ -69,3 +69,26 @@ pub mod newtype_body_endpoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod query_map_endpoint {
|
||||||
|
use ruma_api_macros::ruma_api;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Does something.",
|
||||||
|
method: GET,
|
||||||
|
name: "newtype_body_endpoint",
|
||||||
|
path: "/_matrix/some/query/map/endpoint",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {
|
||||||
|
#[ruma_api(query_map)]
|
||||||
|
pub fields: Vec<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
response {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user