Support optional header values in request/response types
This commit is contained in:
parent
5182015b76
commit
a8b4bad684
@ -136,15 +136,17 @@ impl ToTokens for Api {
|
|||||||
let mut header_kvs = self.request.append_header_kvs();
|
let mut header_kvs = self.request.append_header_kvs();
|
||||||
if requires_authentication.value {
|
if requires_authentication.value {
|
||||||
header_kvs.push(quote! {
|
header_kvs.push(quote! {
|
||||||
#ruma_api_import::exports::http::header::AUTHORIZATION,
|
let req_builder = req_builder.header(
|
||||||
#ruma_api_import::exports::http::header::HeaderValue::from_str(
|
#ruma_api_import::exports::http::header::AUTHORIZATION,
|
||||||
&::std::format!(
|
#ruma_api_import::exports::http::header::HeaderValue::from_str(
|
||||||
"Bearer {}",
|
&::std::format!(
|
||||||
access_token.ok_or(
|
"Bearer {}",
|
||||||
#ruma_api_import::error::IntoHttpError::NeedsAuthentication
|
access_token.ok_or(
|
||||||
)?
|
#ruma_api_import::error::IntoHttpError::NeedsAuthentication
|
||||||
)
|
)?
|
||||||
)?
|
)
|
||||||
|
)?
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,13 +276,14 @@ impl ToTokens for Api {
|
|||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn try_from(response: Response) -> ::std::result::Result<Self, Self::Error> {
|
fn try_from(response: Response) -> ::std::result::Result<Self, Self::Error> {
|
||||||
let response = #ruma_api_import::exports::http::Response::builder()
|
let mut resp_builder = #ruma_api_import::exports::http::Response::builder()
|
||||||
.header(#ruma_api_import::exports::http::header::CONTENT_TYPE, "application/json")
|
.header(#ruma_api_import::exports::http::header::CONTENT_TYPE, "application/json");
|
||||||
#serialize_response_headers
|
|
||||||
.body(#body)
|
#serialize_response_headers
|
||||||
// Since we require header names to come from the `http::header` module,
|
|
||||||
// this cannot fail.
|
// Since we require header names to come from the `http::header` module,
|
||||||
.unwrap();
|
// this cannot fail.
|
||||||
|
let response = resp_builder.body(#body).unwrap();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,16 +344,18 @@ impl ToTokens for Api {
|
|||||||
> {
|
> {
|
||||||
let metadata = <Self as #ruma_api_import::OutgoingRequest>::METADATA;
|
let metadata = <Self as #ruma_api_import::OutgoingRequest>::METADATA;
|
||||||
|
|
||||||
let http_request = #ruma_api_import::exports::http::Request::builder()
|
let mut req_builder = #ruma_api_import::exports::http::Request::builder()
|
||||||
.method(#ruma_api_import::exports::http::Method::#method)
|
.method(#ruma_api_import::exports::http::Method::#method)
|
||||||
.uri(::std::format!(
|
.uri(::std::format!(
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
base_url.strip_suffix("/").unwrap_or(base_url),
|
base_url.strip_suffix("/").unwrap_or(base_url),
|
||||||
#request_path_string,
|
#request_path_string,
|
||||||
#request_query_string,
|
#request_query_string,
|
||||||
))
|
));
|
||||||
#( .header(#header_kvs) )*
|
|
||||||
.body(#request_body)?;
|
#( #header_kvs )*
|
||||||
|
|
||||||
|
let http_request = req_builder.body(#request_body)?;
|
||||||
|
|
||||||
Ok(http_request)
|
Ok(http_request)
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,25 @@ impl Request {
|
|||||||
|
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
|
|
||||||
quote! {
|
match &field.ty {
|
||||||
#import_path::exports::http::header::#header_name,
|
syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. })
|
||||||
#import_path::exports::http::header::HeaderValue::from_str(self.#field_name.as_ref())?
|
if segments.last().unwrap().ident == "Option" =>
|
||||||
|
{
|
||||||
|
quote! {
|
||||||
|
if let Some(header_val) = self.#field_name.as_ref() {
|
||||||
|
req_builder = req_builder.header(
|
||||||
|
#import_path::exports::http::header::#header_name,
|
||||||
|
#import_path::exports::http::header::HeaderValue::from_str(header_val)?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! {
|
||||||
|
req_builder = req_builder.header(
|
||||||
|
#import_path::exports::http::header::#header_name,
|
||||||
|
#import_path::exports::http::header::HeaderValue::from_str(self.#field_name.as_ref())?,
|
||||||
|
);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
@ -68,13 +84,15 @@ impl Request {
|
|||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let header_name_string = header_name.to_string();
|
let header_name_string = header_name.to_string();
|
||||||
|
|
||||||
quote! {
|
let (some_case, none_case) = match &field.ty {
|
||||||
#field_name: match headers
|
syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. })
|
||||||
.get(#import_path::exports::http::header::#header_name)
|
if segments.last().unwrap().ident == "Option" =>
|
||||||
.and_then(|v| v.to_str().ok()) // FIXME: Should have a distinct error message
|
|
||||||
{
|
{
|
||||||
Some(header) => header.to_owned(),
|
(quote! { Some(header.to_owned()) }, quote! { None })
|
||||||
None => {
|
}
|
||||||
|
_ => (
|
||||||
|
quote! { header.to_owned() },
|
||||||
|
quote! {{
|
||||||
use #import_path::exports::serde::de::Error as _;
|
use #import_path::exports::serde::de::Error as _;
|
||||||
|
|
||||||
// FIXME: Not a missing json field, a missing header!
|
// FIXME: Not a missing json field, a missing header!
|
||||||
@ -85,7 +103,17 @@ impl Request {
|
|||||||
request,
|
request,
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#field_name: match headers
|
||||||
|
.get(#import_path::exports::http::header::#header_name)
|
||||||
|
.and_then(|v| v.to_str().ok()) // FIXME: Should have a distinct error message
|
||||||
|
{
|
||||||
|
Some(header) => #some_case,
|
||||||
|
None => #none_case,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -58,15 +58,30 @@ impl Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseField::Header(_, header_name) => {
|
ResponseField::Header(_, header_name) => {
|
||||||
quote_spanned! {span=>
|
let optional_header = match &field.ty {
|
||||||
#field_name: #import_path::try_deserialize!(
|
syn::Type::Path(syn::TypePath {
|
||||||
response,
|
path: syn::Path { segments, .. }, ..
|
||||||
headers.remove(#import_path::exports::http::header::#header_name)
|
}) if segments.last().unwrap().ident == "Option" => {
|
||||||
.expect("response missing expected header")
|
quote! {
|
||||||
.to_str()
|
#field_name: #import_path::try_deserialize!(
|
||||||
|
response,
|
||||||
|
headers.remove(#import_path::exports::http::header::#header_name)
|
||||||
|
.map(|h| h.to_str().map(|s| s.to_owned()))
|
||||||
|
.transpose()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! {
|
||||||
|
#field_name: #import_path::try_deserialize!(
|
||||||
|
response,
|
||||||
|
headers.remove(#import_path::exports::http::header::#header_name)
|
||||||
|
.expect("response missing expected header")
|
||||||
|
.to_str()
|
||||||
)
|
)
|
||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
quote_spanned! {span=> #optional_header }
|
||||||
}
|
}
|
||||||
ResponseField::NewtypeBody(_) => {
|
ResponseField::NewtypeBody(_) => {
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
@ -102,8 +117,29 @@ impl Response {
|
|||||||
field.ident.as_ref().expect("expected field to have an identifier");
|
field.ident.as_ref().expect("expected field to have an identifier");
|
||||||
let span = field.span();
|
let span = field.span();
|
||||||
|
|
||||||
|
let optional_header = match &field.ty {
|
||||||
|
syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. })
|
||||||
|
if segments.last().unwrap().ident == "Option" =>
|
||||||
|
{
|
||||||
|
quote! {
|
||||||
|
if let Some(header) = response.#field_name {
|
||||||
|
resp_builder = resp_builder.header(
|
||||||
|
#import_path::exports::http::header::#header_name,
|
||||||
|
header,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => quote! {
|
||||||
|
resp_builder = resp_builder.header(
|
||||||
|
#import_path::exports::http::header::#header_name,
|
||||||
|
response.#field_name,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
Some(quote_spanned! {span=>
|
Some(quote_spanned! {span=>
|
||||||
.header(#import_path::exports::http::header::#header_name, response.#field_name)
|
#optional_header
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
21
ruma-api/tests/optional_headers.rs
Normal file
21
ruma-api/tests/optional_headers.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use ruma_api::ruma_api;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata: {
|
||||||
|
description: "Does something.",
|
||||||
|
method: GET,
|
||||||
|
name: "no_fields",
|
||||||
|
path: "/_matrix/my/endpoint",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request: {
|
||||||
|
#[ruma_api(header = LOCATION)]
|
||||||
|
location: Option<String>,
|
||||||
|
}
|
||||||
|
response: {
|
||||||
|
#[ruma_api(header = LOCATION)]
|
||||||
|
stuff: Option<String>,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user