Split the Error type in two

This commit is contained in:
Jonas Platte 2020-02-07 01:00:07 +01:00
parent 8a7e533fed
commit 542bef8c4a
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
5 changed files with 89 additions and 99 deletions

View File

@ -1,7 +1,7 @@
language: "rust" language: "rust"
cache: "cargo" cache: "cargo"
rust: rust:
- 1.39.0 - 1.40.0
- stable - stable
- beta - beta
- nightly - nightly

View File

@ -1,5 +1,12 @@
# [unreleased] # [unreleased]
Breaking changes:
* Instead of one `Error` type, there is now two: `FromHttpError` and `IntoHttpError`
* In constrast to the old `Error` types, these can be pattern matched and generally allow getting
much more context for why things failed
* Out Minimum Supported Rust Version is now 1.40.0
# 0.12.1 # 0.12.1
Improvements: Improvements:

View File

@ -16,7 +16,7 @@ edition = "2018"
http = "0.2.0" http = "0.2.0"
percent-encoding = { version = "2.1.0", optional = true } percent-encoding = { version = "2.1.0", optional = true }
ruma-api-macros = { version = "=0.9.1", path = "ruma-api-macros", optional = true } ruma-api-macros = { version = "=0.9.1", path = "ruma-api-macros", optional = true }
ruma-identifiers = "0.14.0" ruma-identifiers = { version = "0.14.0", optional = true }
serde = { version = "1.0.103", features = ["derive"], optional = true } serde = { version = "1.0.103", features = ["derive"], optional = true }
serde_json = "1.0.42" serde_json = "1.0.42"
serde_urlencoded = "0.6.1" serde_urlencoded = "0.6.1"
@ -27,7 +27,13 @@ ruma-events = "0.15.1"
[features] [features]
default = ["with-ruma-api-macros"] default = ["with-ruma-api-macros"]
with-ruma-api-macros = ["percent-encoding", "ruma-api-macros", "serde", "url"] with-ruma-api-macros = [
"percent-encoding",
"ruma-api-macros",
"ruma-identifiers",
"serde",
"url",
]
[workspace] [workspace]
members = [ members = [

View File

@ -369,7 +369,7 @@ impl ToTokens for Api {
#request_type #request_type
impl std::convert::TryFrom<ruma_api::exports::http::Request<Vec<u8>>> for #request_try_from_type { impl std::convert::TryFrom<ruma_api::exports::http::Request<Vec<u8>>> for #request_try_from_type {
type Error = ruma_api::Error; type Error = ruma_api::FromHttpError;
#[allow(unused_variables)] #[allow(unused_variables)]
fn try_from(request: ruma_api::exports::http::Request<Vec<u8>>) -> Result<Self, Self::Error> { fn try_from(request: ruma_api::exports::http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
@ -388,7 +388,7 @@ impl ToTokens for Api {
} }
impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> { impl std::convert::TryFrom<Request> for ruma_api::exports::http::Request<Vec<u8>> {
type Error = ruma_api::Error; type Error = ruma_api::IntoHttpError;
#[allow(unused_mut, unused_variables)] #[allow(unused_mut, unused_variables)]
fn try_from(request: Request) -> Result<Self, Self::Error> { fn try_from(request: Request) -> Result<Self, Self::Error> {
@ -419,7 +419,7 @@ impl ToTokens for Api {
#response_type #response_type
impl std::convert::TryFrom<Response> for ruma_api::exports::http::Response<Vec<u8>> { impl std::convert::TryFrom<Response> for ruma_api::exports::http::Response<Vec<u8>> {
type Error = ruma_api::Error; type Error = ruma_api::IntoHttpError;
#[allow(unused_variables)] #[allow(unused_variables)]
fn try_from(response: Response) -> Result<Self, Self::Error> { fn try_from(response: Response) -> Result<Self, Self::Error> {
@ -433,7 +433,7 @@ impl ToTokens for Api {
} }
impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for #response_try_from_type { impl std::convert::TryFrom<ruma_api::exports::http::Response<Vec<u8>>> for #response_try_from_type {
type Error = ruma_api::Error; type Error = ruma_api::FromHttpError;
#[allow(unused_variables)] #[allow(unused_variables)]
fn try_from( fn try_from(
@ -449,7 +449,7 @@ impl ToTokens for Api {
#response_init_fields #response_init_fields
}) })
} else { } else {
Err(http_response.status().clone().into()) Err(ruma_api::FromHttpError::Http(http_response))
} }
} }
} }

View File

@ -17,11 +17,9 @@ use std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
error::Error as StdError, error::Error as StdError,
fmt::{Display, Formatter, Result as FmtResult}, fmt::{Display, Formatter, Result as FmtResult},
io,
}; };
use http::{self, Method, StatusCode}; use http::{self, Method};
use ruma_identifiers;
use serde_json; use serde_json;
use serde_urlencoded; use serde_urlencoded;
@ -240,111 +238,89 @@ pub trait Outgoing {
/// A Matrix API endpoint. /// A Matrix API endpoint.
/// ///
/// The type implementing this trait contains any data needed to make a request to the endpoint. /// The type implementing this trait contains any data needed to make a request to the endpoint.
pub trait Endpoint: Outgoing + TryInto<http::Request<Vec<u8>>, Error = Error> pub trait Endpoint: Outgoing + TryInto<http::Request<Vec<u8>>, Error = IntoHttpError>
where where
<Self as Outgoing>::Incoming: TryFrom<http::Request<Vec<u8>>, Error = Error>, <Self as Outgoing>::Incoming: TryFrom<http::Request<Vec<u8>>, Error = FromHttpError>,
<Self::Response as Outgoing>::Incoming: TryFrom<http::Response<Vec<u8>>, Error = Error>, <Self::Response as Outgoing>::Incoming: TryFrom<http::Response<Vec<u8>>, Error = FromHttpError>,
{ {
/// Data returned in a successful response from the endpoint. /// Data returned in a successful response from the endpoint.
type Response: Outgoing + TryInto<http::Response<Vec<u8>>, Error = Error>; type Response: Outgoing + TryInto<http::Response<Vec<u8>>, Error = IntoHttpError>;
/// Metadata about the endpoint. /// Metadata about the endpoint.
const METADATA: Metadata; const METADATA: Metadata;
} }
/// An error when converting an `Endpoint` request or response to the corresponding type from the /// An error when converting an `http` request or response to the corresponding
/// `http` crate.
#[derive(Debug)] #[derive(Debug)]
pub struct Error(pub(crate) InnerError); #[non_exhaustive]
pub enum FromHttpError {
/// The server returned a non-success status
Http(http::Response<Vec<u8>>),
/// JSON deserialization failed
Json(serde_json::Error),
/// Deserialization of query parameters failed
Query(serde_urlencoded::de::Error),
}
impl Display for Error { /// An error when converting a request or response to the corresponding http type.
#[derive(Debug)]
#[non_exhaustive]
pub enum IntoHttpError {
/// JSON serialization failed
Json(serde_json::Error),
/// Serialization of query parameters failed
Query(serde_urlencoded::ser::Error),
}
impl From<serde_json::Error> for FromHttpError {
fn from(err: serde_json::Error) -> Self {
Self::Json(err)
}
}
impl From<serde_urlencoded::de::Error> for FromHttpError {
fn from(err: serde_urlencoded::de::Error) -> Self {
Self::Query(err)
}
}
impl Display for FromHttpError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let message = match self.0 { match self {
InnerError::Http(_) => "An error converting to or from `http` types occurred.".into(), Self::Http(res) => match res.status().canonical_reason() {
InnerError::Io(_) => "An I/O error occurred.".into(), Some(reason) => write!(f, "HTTP status {} {}", res.status().as_str(), reason),
InnerError::SerdeJson(_) => "A JSON error occurred.".into(), None => write!(f, "HTTP status {}", res.status().as_str()),
InnerError::SerdeUrlEncodedDe(_) => { },
"A URL encoding deserialization error occurred.".into() Self::Json(err) => write!(f, "JSON deserialization failed: {}", err),
} Self::Query(err) => write!(f, "Query parameter deserialization failed: {}", err),
InnerError::SerdeUrlEncodedSer(_) => { }
"A URL encoding serialization error occurred.".into()
}
InnerError::RumaIdentifiers(_) => "A ruma-identifiers error occurred.".into(),
InnerError::StatusCode(code) => format!("A HTTP {} error occurred.", code),
};
write!(f, "{}", message)
} }
} }
impl StdError for Error {} impl StdError for FromHttpError {}
/// Internal representation of errors. impl From<serde_json::Error> for IntoHttpError {
#[derive(Debug)] fn from(err: serde_json::Error) -> Self {
pub(crate) enum InnerError { Self::Json(err)
/// An HTTP error.
Http(http::Error),
/// A I/O error.
Io(io::Error),
/// A Serde JSON error.
SerdeJson(serde_json::Error),
/// A Serde URL decoding error.
SerdeUrlEncodedDe(serde_urlencoded::de::Error),
/// A Serde URL encoding error.
SerdeUrlEncodedSer(serde_urlencoded::ser::Error),
/// A Ruma Identitifiers error.
RumaIdentifiers(ruma_identifiers::Error),
/// An HTTP status code indicating error.
StatusCode(StatusCode),
}
impl From<http::Error> for Error {
fn from(error: http::Error) -> Self {
Self(InnerError::Http(error))
} }
} }
impl From<io::Error> for Error { impl From<serde_urlencoded::ser::Error> for IntoHttpError {
fn from(error: io::Error) -> Self { fn from(err: serde_urlencoded::ser::Error) -> Self {
Self(InnerError::Io(error)) Self::Query(err)
} }
} }
impl From<serde_json::Error> for Error { impl Display for IntoHttpError {
fn from(error: serde_json::Error) -> Self { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
Self(InnerError::SerdeJson(error)) match self {
Self::Json(err) => write!(f, "JSON serialization failed: {}", err),
Self::Query(err) => write!(f, "Query parameter serialization failed: {}", err),
}
} }
} }
impl From<serde_urlencoded::de::Error> for Error { impl StdError for IntoHttpError {}
fn from(error: serde_urlencoded::de::Error) -> Self {
Self(InnerError::SerdeUrlEncodedDe(error))
}
}
impl From<serde_urlencoded::ser::Error> for Error {
fn from(error: serde_urlencoded::ser::Error) -> Self {
Self(InnerError::SerdeUrlEncodedSer(error))
}
}
impl From<ruma_identifiers::Error> for Error {
fn from(error: ruma_identifiers::Error) -> Self {
Self(InnerError::RumaIdentifiers(error))
}
}
impl From<StatusCode> for Error {
fn from(error: StatusCode) -> Self {
Self(InnerError::StatusCode(error))
}
}
/// Metadata about an API endpoint. /// Metadata about an API endpoint.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -381,7 +357,7 @@ mod tests {
use serde::{de::IntoDeserializer, Deserialize, Serialize}; use serde::{de::IntoDeserializer, Deserialize, Serialize};
use serde_json; use serde_json;
use crate::{Endpoint, Error, Metadata, Outgoing}; use crate::{Endpoint, FromHttpError, IntoHttpError, Metadata, Outgoing};
/// A request to create a new room alias. /// A request to create a new room alias.
#[derive(Debug)] #[derive(Debug)]
@ -408,7 +384,7 @@ mod tests {
} }
impl TryFrom<Request> for http::Request<Vec<u8>> { impl TryFrom<Request> for http::Request<Vec<u8>> {
type Error = Error; type Error = IntoHttpError;
fn try_from(request: Request) -> Result<http::Request<Vec<u8>>, Self::Error> { fn try_from(request: Request) -> Result<http::Request<Vec<u8>>, Self::Error> {
let metadata = Request::METADATA; let metadata = Request::METADATA;
@ -423,14 +399,15 @@ mod tests {
let http_request = http::Request::builder() let http_request = http::Request::builder()
.method(metadata.method) .method(metadata.method)
.uri(path) .uri(path)
.body(serde_json::to_vec(&request_body).map_err(Error::from)?)?; .body(serde_json::to_vec(&request_body)?)
.expect("http request building to succeed");
Ok(http_request) Ok(http_request)
} }
} }
impl TryFrom<http::Request<Vec<u8>>> for Request { impl TryFrom<http::Request<Vec<u8>>> for Request {
type Error = Error; type Error = FromHttpError;
fn try_from(request: http::Request<Vec<u8>>) -> Result<Self, Self::Error> { fn try_from(request: http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
let request_body: RequestBody = let request_body: RequestBody =
@ -462,19 +439,19 @@ mod tests {
} }
impl TryFrom<http::Response<Vec<u8>>> for Response { impl TryFrom<http::Response<Vec<u8>>> for Response {
type Error = Error; type Error = FromHttpError;
fn try_from(http_response: http::Response<Vec<u8>>) -> Result<Response, Self::Error> { fn try_from(http_response: http::Response<Vec<u8>>) -> Result<Response, Self::Error> {
if http_response.status().is_success() { if http_response.status().is_success() {
Ok(Response) Ok(Response)
} else { } else {
Err(http_response.status().into()) Err(FromHttpError::Http(http_response))
} }
} }
} }
impl TryFrom<Response> for http::Response<Vec<u8>> { impl TryFrom<Response> for http::Response<Vec<u8>> {
type Error = Error; type Error = IntoHttpError;
fn try_from(_: Response) -> Result<http::Response<Vec<u8>>, Self::Error> { fn try_from(_: Response) -> Result<http::Response<Vec<u8>>, Self::Error> {
let response = http::Response::builder() let response = http::Response::builder()