api: Introduce OutgoingResponse trait

This commit is contained in:
Jonas Platte 2021-04-10 15:57:07 +02:00
parent 6f5c1ee953
commit b122dcc135
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
6 changed files with 34 additions and 32 deletions

View File

@ -116,7 +116,7 @@ impl Response {
if segments.last().unwrap().ident == "Option" => if segments.last().unwrap().ident == "Option" =>
{ {
quote! { quote! {
if let Some(header) = response.#field_name { if let Some(header) = self.#field_name {
headers headers
.insert( .insert(
#http::header::#header_name, #http::header::#header_name,
@ -129,7 +129,7 @@ impl Response {
headers headers
.insert( .insert(
#http::header::#header_name, #http::header::#header_name,
response.#field_name.parse()?, self.#field_name.parse()?,
); );
}, },
}; };
@ -152,13 +152,13 @@ impl Response {
if let Some(field) = self.newtype_raw_body_field() { if let Some(field) = self.newtype_raw_body_field() {
let field_name = field.ident.as_ref().expect("expected field to have an identifier"); let field_name = field.ident.as_ref().expect("expected field to have an identifier");
let span = field.span(); let span = field.span();
return quote_spanned!(span=> response.#field_name); return quote_spanned!(span=> self.#field_name);
} }
let body = if let Some(field) = self.newtype_body_field() { let body = if let Some(field) = self.newtype_body_field() {
let field_name = field.ident.as_ref().expect("expected field to have an identifier"); let field_name = field.ident.as_ref().expect("expected field to have an identifier");
let span = field.span(); let span = field.span();
quote_spanned!(span=> response.#field_name) quote_spanned!(span=> self.#field_name)
} else { } else {
let fields = self.fields.iter().filter_map(|response_field| { let fields = self.fields.iter().filter_map(|response_field| {
if let ResponseField::Body(ref field) = *response_field { if let ResponseField::Body(ref field) = *response_field {
@ -170,7 +170,7 @@ impl Response {
Some(quote_spanned! {span=> Some(quote_spanned! {span=>
#( #cfg_attrs )* #( #cfg_attrs )*
#field_name: response.#field_name #field_name: self.#field_name
}) })
} else { } else {
None None
@ -285,10 +285,13 @@ impl Response {
#[automatically_derived] #[automatically_derived]
#[cfg(feature = "server")] #[cfg(feature = "server")]
impl ::std::convert::TryFrom<Response> for #http::Response<Vec<u8>> { impl #ruma_api::OutgoingResponse for Response {
type Error = #ruma_api::error::IntoHttpError; fn try_into_http_response(
self,
fn try_from(response: Response) -> ::std::result::Result<Self, Self::Error> { ) -> ::std::result::Result<
#http::Response<::std::vec::Vec<u8>>,
#ruma_api::error::IntoHttpError,
> {
let mut resp_builder = #http::Response::builder() let mut resp_builder = #http::Response::builder()
.header(#http::header::CONTENT_TYPE, "application/json"); .header(#http::header::CONTENT_TYPE, "application/json");
@ -300,8 +303,7 @@ impl Response {
// This cannot fail because we parse each header value // This cannot fail because we parse each header value
// checking for errors as each value is inserted and // checking for errors as each value is inserted and
// we only allow keys from the `http::header` module. // we only allow keys from the `http::header` module.
let response = resp_builder.body(#body).unwrap(); Ok(resp_builder.body(#body).unwrap())
Ok(response)
} }
} }

View File

@ -20,7 +20,7 @@
#[cfg(not(all(feature = "client", feature = "server")))] #[cfg(not(all(feature = "client", feature = "server")))]
compile_error!("ruma_api's Cargo features only exist as a workaround are not meant to be disabled"); compile_error!("ruma_api's Cargo features only exist as a workaround are not meant to be disabled");
use std::{convert::TryInto, error::Error as StdError}; use std::{convert::TryInto as _, error::Error as StdError};
use bytes::Buf; use bytes::Buf;
use http::Method; use http::Method;
@ -306,7 +306,7 @@ pub trait IncomingRequest: Sized {
type EndpointError: EndpointError; type EndpointError: EndpointError;
/// Response type to return when the request is successful. /// Response type to return when the request is successful.
type OutgoingResponse: TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>; type OutgoingResponse: OutgoingResponse;
/// Metadata about the endpoint. /// Metadata about the endpoint.
const METADATA: Metadata; const METADATA: Metadata;
@ -315,6 +315,15 @@ pub trait IncomingRequest: Sized {
fn try_from_http_request(req: http::Request<Vec<u8>>) -> Result<Self, FromHttpRequestError>; fn try_from_http_request(req: http::Request<Vec<u8>>) -> Result<Self, FromHttpRequestError>;
} }
/// A request type for a Matrix API endpoint, used for sending responses.
pub trait OutgoingResponse {
/// Tries to convert this response into an `http::Response`.
///
/// This method should only fail when when invalid header values are specified. It may also
/// fail with a serialization error in case of bugs in Ruma though.
fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError>;
}
/// Marker trait for requests that don't require authentication, for the client side. /// Marker trait for requests that don't require authentication, for the client side.
pub trait OutgoingNonAuthRequest: OutgoingRequest {} pub trait OutgoingNonAuthRequest: OutgoingRequest {}

View File

@ -1,7 +1,5 @@
use std::convert::TryFrom;
use http::header::{Entry, CONTENT_TYPE}; use http::header::{Entry, CONTENT_TYPE};
use ruma_api::{ruma_api, OutgoingRequest as _}; use ruma_api::{ruma_api, OutgoingRequest as _, OutgoingResponse as _};
ruma_api! { ruma_api! {
metadata: { metadata: {
@ -30,7 +28,7 @@ ruma_api! {
#[test] #[test]
fn response_content_type_override() { fn response_content_type_override() {
let res = Response { stuff: "magic".into() }; let res = Response { stuff: "magic".into() };
let mut http_res = http::Response::<Vec<u8>>::try_from(res).unwrap(); let mut http_res = res.try_into_http_response().unwrap();
// Test that we correctly replaced the default content type, // Test that we correctly replaced the default content type,
// not adding another content-type header. // not adding another content-type header.

View File

@ -7,6 +7,7 @@ use http::{header::CONTENT_TYPE, method::Method};
use ruma_api::{ use ruma_api::{
error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError, ServerError, Void}, error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError, ServerError, Void},
AuthScheme, EndpointError, IncomingRequest, IncomingResponse, Metadata, OutgoingRequest, AuthScheme, EndpointError, IncomingRequest, IncomingResponse, Metadata, OutgoingRequest,
OutgoingResponse,
}; };
use ruma_identifiers::{RoomAliasId, RoomId}; use ruma_identifiers::{RoomAliasId, RoomId};
use ruma_serde::Outgoing; use ruma_serde::Outgoing;
@ -113,10 +114,8 @@ impl IncomingResponse for Response {
} }
} }
impl TryFrom<Response> for http::Response<Vec<u8>> { impl OutgoingResponse for Response {
type Error = IntoHttpError; fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
fn try_from(_: Response) -> Result<http::Response<Vec<u8>>, Self::Error> {
let response = http::Response::builder() let response = http::Response::builder()
.header(CONTENT_TYPE, "application/json") .header(CONTENT_TYPE, "application/json")
.body(b"{}".to_vec()) .body(b"{}".to_vec())

View File

@ -1,6 +1,4 @@
use std::convert::TryFrom; use ruma_api::{ruma_api, OutgoingRequest as _, OutgoingResponse as _};
use ruma_api::{ruma_api, OutgoingRequest as _};
ruma_api! { ruma_api! {
metadata: { metadata: {
@ -27,7 +25,7 @@ fn empty_request_http_repr() {
#[test] #[test]
fn empty_response_http_repr() { fn empty_response_http_repr() {
let res = Response {}; let res = Response {};
let http_res = http::Response::<Vec<u8>>::try_from(res).unwrap(); let http_res = res.try_into_http_response().unwrap();
assert_eq!(http_res.body(), b"{}"); assert_eq!(http_res.body(), b"{}");
} }

View File

@ -1,9 +1,7 @@
use std::convert::TryFrom;
use bytes::Buf; use bytes::Buf;
use ruma_api::{ use ruma_api::{
error::{FromHttpResponseError, IntoHttpError, Void}, error::{FromHttpResponseError, IntoHttpError, Void},
ruma_api, IncomingResponse, ruma_api, IncomingResponse, OutgoingResponse,
}; };
use ruma_serde::Outgoing; use ruma_serde::Outgoing;
@ -37,10 +35,8 @@ impl IncomingResponse for Response {
} }
} }
impl TryFrom<Response> for http::Response<Vec<u8>> { impl OutgoingResponse for Response {
type Error = IntoHttpError; fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
fn try_from(_: Response) -> Result<Self, Self::Error> {
todo!() todo!()
} }
} }