Split the Error
type in two
This commit is contained in:
parent
8a7e533fed
commit
542bef8c4a
@ -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
|
||||||
|
@ -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:
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -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 = [
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
159
src/lib.rs
159
src/lib.rs
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user