api: Make OutgoingResponse a supertrait of EndpointError

This commit is contained in:
Jonas Platte 2021-04-13 13:15:57 +02:00
parent cc2f2a231b
commit 6585aeb628
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
6 changed files with 48 additions and 34 deletions

View File

@ -134,7 +134,9 @@ impl Response {
#response_init_fields #response_init_fields
}) })
} else { } else {
match <#error_ty as #ruma_api::EndpointError>::try_from_response(response) { match <#error_ty as #ruma_api::EndpointError>::try_from_http_response(
response
) {
Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()), Ok(err) => Err(#ruma_api::error::ServerError::Known(err).into()),
Err(response_err) => { Err(response_err) => {
Err(#ruma_api::error::ServerError::Unknown(response_err).into()) Err(#ruma_api::error::ServerError::Unknown(response_err).into())

View File

@ -7,15 +7,21 @@ use std::{error::Error as StdError, fmt};
use bytes::Buf; use bytes::Buf;
use thiserror::Error; use thiserror::Error;
use crate::EndpointError; use crate::{EndpointError, OutgoingResponse};
// FIXME when `!` becomes stable use it // FIXME when `!` becomes stable use it
/// Default `EndpointError` for `ruma_api!` macro /// Default `EndpointError` for `ruma_api!` macro
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Void {} pub enum Void {}
impl OutgoingResponse for Void {
fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
match self {}
}
}
impl EndpointError for Void { impl EndpointError for Void {
fn try_from_response<T: Buf>( fn try_from_http_response<T: Buf>(
_response: http::Response<T>, _response: http::Response<T>,
) -> Result<Self, ResponseDeserializationError> { ) -> Result<Self, ResponseDeserializationError> {
Err(ResponseDeserializationError::none()) Err(ResponseDeserializationError::none())

View File

@ -214,17 +214,6 @@ pub mod exports {
use error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError}; use error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError};
/// Gives users the ability to define their own serializable / deserializable errors.
pub trait EndpointError: StdError + Sized + 'static {
/// Tries to construct `Self` from an `http::Response`.
///
/// This will always return `Err` variant when no `error` field is defined in
/// the `ruma_api` macro.
fn try_from_response<T: Buf>(
response: http::Response<T>,
) -> Result<Self, error::ResponseDeserializationError>;
}
/// A request type for a Matrix API endpoint, used for sending requests. /// A request type for a Matrix API endpoint, used for sending requests.
pub trait OutgoingRequest: Sized { pub trait OutgoingRequest: Sized {
/// A type capturing the expected error conditions the server can return. /// A type capturing the expected error conditions the server can return.
@ -324,6 +313,17 @@ pub trait OutgoingResponse {
fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError>; fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError>;
} }
/// Gives users the ability to define their own serializable / deserializable errors.
pub trait EndpointError: OutgoingResponse + StdError + Sized + 'static {
/// Tries to construct `Self` from an `http::Response`.
///
/// This will always return `Err` variant when no `error` field is defined in
/// the `ruma_api` macro.
fn try_from_http_response<T: Buf>(
response: http::Response<T>,
) -> Result<Self, error::ResponseDeserializationError>;
}
/// 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

@ -107,7 +107,7 @@ impl IncomingResponse for Response {
Ok(Response) Ok(Response)
} else { } else {
Err(FromHttpResponseError::Http(ServerError::Known( Err(FromHttpResponseError::Http(ServerError::Known(
<Void as EndpointError>::try_from_response(http_response)?, <Void as EndpointError>::try_from_http_response(http_response)?,
))) )))
} }
} }

View File

@ -3,7 +3,10 @@
use std::{collections::BTreeMap, fmt, time::Duration}; use std::{collections::BTreeMap, fmt, time::Duration};
use bytes::Buf; use bytes::Buf;
use ruma_api::{error::ResponseDeserializationError, EndpointError}; use ruma_api::{
error::{IntoHttpError, ResponseDeserializationError},
EndpointError, OutgoingResponse,
};
use ruma_identifiers::RoomVersionId; use ruma_identifiers::RoomVersionId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{from_reader as from_json_reader, to_vec as to_json_vec, Value as JsonValue}; use serde_json::{from_reader as from_json_reader, to_vec as to_json_vec, Value as JsonValue};
@ -203,7 +206,7 @@ pub struct Error {
} }
impl EndpointError for Error { impl EndpointError for Error {
fn try_from_response<T: Buf>( fn try_from_http_response<T: Buf>(
response: http::Response<T>, response: http::Response<T>,
) -> Result<Self, ResponseDeserializationError> { ) -> Result<Self, ResponseDeserializationError> {
let status = response.status(); let status = response.status();
@ -233,13 +236,13 @@ impl ErrorBody {
} }
} }
impl From<Error> for http::Response<Vec<u8>> { impl OutgoingResponse for Error {
fn from(error: Error) -> http::Response<Vec<u8>> { fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
http::Response::builder() http::Response::builder()
.header(http::header::CONTENT_TYPE, "application/json") .header(http::header::CONTENT_TYPE, "application/json")
.status(error.status_code) .status(self.status_code)
.body(to_json_vec(&ErrorBody::from(error)).unwrap()) .body(to_json_vec(&ErrorBody::from(self))?)
.unwrap() .map_err(Into::into)
} }
} }

View File

@ -3,7 +3,10 @@
use std::{collections::BTreeMap, fmt}; use std::{collections::BTreeMap, fmt};
use bytes::Buf; use bytes::Buf;
use ruma_api::{error::ResponseDeserializationError, EndpointError}; use ruma_api::{
error::{IntoHttpError, ResponseDeserializationError},
EndpointError, OutgoingResponse,
};
use ruma_serde::Outgoing; use ruma_serde::Outgoing;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{ use serde_json::{
@ -134,28 +137,28 @@ impl From<MatrixError> for UiaaResponse {
} }
impl EndpointError for UiaaResponse { impl EndpointError for UiaaResponse {
fn try_from_response<T: Buf>( fn try_from_http_response<T: Buf>(
response: http::Response<T>, response: http::Response<T>,
) -> Result<Self, ResponseDeserializationError> { ) -> Result<Self, ResponseDeserializationError> {
if response.status() == http::StatusCode::UNAUTHORIZED { if response.status() == http::StatusCode::UNAUTHORIZED {
Ok(UiaaResponse::AuthResponse(from_json_reader(response.into_body().reader())?)) Ok(UiaaResponse::AuthResponse(from_json_reader(response.into_body().reader())?))
} else { } else {
MatrixError::try_from_response(response).map(From::from) MatrixError::try_from_http_response(response).map(From::from)
} }
} }
} }
impl std::error::Error for UiaaResponse {} impl std::error::Error for UiaaResponse {}
impl From<UiaaResponse> for http::Response<Vec<u8>> { impl OutgoingResponse for UiaaResponse {
fn from(uiaa_response: UiaaResponse) -> http::Response<Vec<u8>> { fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
match uiaa_response { match self {
UiaaResponse::AuthResponse(authentication_info) => http::Response::builder() UiaaResponse::AuthResponse(authentication_info) => http::Response::builder()
.header(http::header::CONTENT_TYPE, "application/json") .header(http::header::CONTENT_TYPE, "application/json")
.status(&http::StatusCode::UNAUTHORIZED) .status(&http::StatusCode::UNAUTHORIZED)
.body(to_json_vec(&authentication_info).unwrap()) .body(to_json_vec(&authentication_info)?)
.unwrap(), .map_err(Into::into),
UiaaResponse::MatrixError(error) => http::Response::from(error), UiaaResponse::MatrixError(error) => error.try_into_http_response(),
} }
} }
} }
@ -164,7 +167,7 @@ impl From<UiaaResponse> for http::Response<Vec<u8>> {
mod tests { mod tests {
use maplit::btreemap; use maplit::btreemap;
use matches::assert_matches; use matches::assert_matches;
use ruma_api::EndpointError; use ruma_api::{EndpointError, OutgoingResponse};
use serde_json::{ use serde_json::{
from_slice as from_json_slice, from_str as from_json_str, from_value as from_json_value, from_slice as from_json_slice, from_str as from_json_str, from_value as from_json_value,
json, to_value as to_json_value, value::to_raw_value as to_raw_json_value, json, to_value as to_json_value, value::to_raw_value as to_raw_json_value,
@ -334,7 +337,7 @@ mod tests {
session: None, session: None,
auth_error: None, auth_error: None,
}; };
let uiaa_response: http::Response<Vec<u8>> = UiaaResponse::AuthResponse(uiaa_info).into(); let uiaa_response = UiaaResponse::AuthResponse(uiaa_info).try_into_http_response();
assert_matches!( assert_matches!(
from_json_slice::<UiaaInfo>(uiaa_response.body()).unwrap(), from_json_slice::<UiaaInfo>(uiaa_response.body()).unwrap(),
@ -385,7 +388,7 @@ mod tests {
.body(json.as_bytes()) .body(json.as_bytes())
.unwrap(); .unwrap();
let parsed_uiaa_info = match UiaaResponse::try_from_response(http_response).unwrap() { let parsed_uiaa_info = match UiaaResponse::try_from_http_response(http_response).unwrap() {
UiaaResponse::AuthResponse(uiaa_info) => uiaa_info, UiaaResponse::AuthResponse(uiaa_info) => uiaa_info,
_ => panic!("Expected UiaaResponse::AuthResponse"), _ => panic!("Expected UiaaResponse::AuthResponse"),
}; };