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
})
} 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()),
Err(response_err) => {
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 thiserror::Error;
use crate::EndpointError;
use crate::{EndpointError, OutgoingResponse};
// FIXME when `!` becomes stable use it
/// Default `EndpointError` for `ruma_api!` macro
#[derive(Clone, Copy, Debug)]
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 {
fn try_from_response<T: Buf>(
fn try_from_http_response<T: Buf>(
_response: http::Response<T>,
) -> Result<Self, ResponseDeserializationError> {
Err(ResponseDeserializationError::none())

View File

@ -214,17 +214,6 @@ pub mod exports {
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.
pub trait OutgoingRequest: Sized {
/// 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>;
}
/// 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.
pub trait OutgoingNonAuthRequest: OutgoingRequest {}

View File

@ -107,7 +107,7 @@ impl IncomingResponse for Response {
Ok(Response)
} else {
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 bytes::Buf;
use ruma_api::{error::ResponseDeserializationError, EndpointError};
use ruma_api::{
error::{IntoHttpError, ResponseDeserializationError},
EndpointError, OutgoingResponse,
};
use ruma_identifiers::RoomVersionId;
use serde::{Deserialize, Serialize};
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 {
fn try_from_response<T: Buf>(
fn try_from_http_response<T: Buf>(
response: http::Response<T>,
) -> Result<Self, ResponseDeserializationError> {
let status = response.status();
@ -233,13 +236,13 @@ impl ErrorBody {
}
}
impl From<Error> for http::Response<Vec<u8>> {
fn from(error: Error) -> http::Response<Vec<u8>> {
impl OutgoingResponse for Error {
fn try_into_http_response(self) -> Result<http::Response<Vec<u8>>, IntoHttpError> {
http::Response::builder()
.header(http::header::CONTENT_TYPE, "application/json")
.status(error.status_code)
.body(to_json_vec(&ErrorBody::from(error)).unwrap())
.unwrap()
.status(self.status_code)
.body(to_json_vec(&ErrorBody::from(self))?)
.map_err(Into::into)
}
}

View File

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