Redesign Error type

- The `Error`'s kind is a now a set of variants depending on the context
  of when an error could occur.
- If another error was the cause, it is now always the `source`.

Along with the `is_*` methods, this should help in understanding *when*
a certain error occurred. For example, an error setting the TLS
certificates will return a builder error, with the TLS error as the
source. This should help differentiate from a TLS error that happens
when connecting to a server.

It also makes the internal code less dependent on all the exact
dependencies that can be enabled or disabled.
This commit is contained in:
Sean McArthur
2019-09-17 12:55:20 -07:00
parent 6b5be07158
commit 53495e1526
20 changed files with 209 additions and 544 deletions

View File

@@ -258,9 +258,8 @@ async fn send_future(sender: Sender) -> Result<(), crate::Error> {
buf.advance_mut(n);
},
Err(e) => {
let ret = io::Error::new(e.kind(), e.to_string());
tx.take().expect("tx only taken on error").abort();
return Err(crate::error::from(ret));
return Err(crate::error::body(e));
}
}
}
@@ -273,7 +272,7 @@ async fn send_future(sender: Sender) -> Result<(), crate::Error> {
.expect("tx only taken on error")
.send_data(buf.take().freeze().into())
.await
.map_err(crate::error::from)?;
.map_err(crate::error::body)?;
written += buf_len;
}

View File

@@ -550,7 +550,7 @@ impl ClientHandle {
.spawn(move || {
use tokio::runtime::current_thread::Runtime;
let mut rt = match Runtime::new().map_err(crate::error::from) {
let mut rt = match Runtime::new().map_err(crate::error::builder) {
Err(e) => {
if let Err(e) = spawn_tx.send(Err(e)) {
error!("Failed to communicate runtime creation failure: {:?}", e);
@@ -587,7 +587,7 @@ impl ClientHandle {
rt.block_on(f)
})
.map_err(crate::error::from)?;
.map_err(crate::error::builder)?;
// Wait for the runtime thread to start up...
match wait::timeout(spawn_rx, None) {
@@ -639,8 +639,8 @@ impl ClientHandle {
self.timeout.0,
KeepCoreThreadAlive(Some(self.inner.clone())),
)),
Err(wait::Waited::TimedOut) => Err(crate::error::timedout(Some(url))),
Err(wait::Waited::Executor(err)) => Err(crate::error::from(err).with_url(url)),
Err(wait::Waited::TimedOut(e)) => Err(crate::error::request(e).with_url(url)),
Err(wait::Waited::Executor(err)) => Err(crate::error::request(err).with_url(url)),
Err(wait::Waited::Inner(err)) => Err(err.with_url(url)),
}
}

View File

@@ -60,8 +60,6 @@ mod request;
mod response;
mod wait;
pub(crate) use self::wait::Waited;
pub use self::body::Body;
pub use self::client::{Client, ClientBuilder};
pub use self::request::{Request, RequestBuilder};

View File

@@ -234,7 +234,7 @@ impl Part {
/// Tries to set the mime of this part.
pub fn mime_str(self, mime: &str) -> crate::Result<Part> {
Ok(self.mime(mime.parse().map_err(crate::error::from)?))
Ok(self.mime(mime.parse().map_err(crate::error::builder)?))
}
// Re-export when mime 0.4 is available, with split MediaType/MediaRange.

View File

@@ -149,9 +149,9 @@ impl RequestBuilder {
Ok(value) => {
req.headers_mut().append(key, value);
}
Err(e) => error = Some(crate::error::from(e.into())),
Err(e) => error = Some(crate::error::builder(e.into())),
},
Err(e) => error = Some(crate::error::from(e.into())),
Err(e) => error = Some(crate::error::builder(e.into())),
};
}
if let Some(err) = error {
@@ -323,7 +323,7 @@ impl RequestBuilder {
let serializer = serde_urlencoded::Serializer::new(&mut pairs);
if let Err(err) = query.serialize(serializer) {
error = Some(crate::error::from(err));
error = Some(crate::error::builder(err));
}
}
if let Ok(ref mut req) = self.request {
@@ -374,7 +374,7 @@ impl RequestBuilder {
);
*req.body_mut() = Some(body.into());
}
Err(err) => error = Some(crate::error::from(err)),
Err(err) => error = Some(crate::error::builder(err)),
}
}
if let Some(err) = error {
@@ -417,7 +417,7 @@ impl RequestBuilder {
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
*req.body_mut() = Some(body.into());
}
Err(err) => error = Some(crate::error::from(err)),
Err(err) => error = Some(crate::error::builder(err)),
}
}
if let Some(err) = error {
@@ -797,8 +797,9 @@ mod tests {
#[test]
fn add_json_fail() {
use serde::ser::Error;
use serde::ser::Error as _;
use serde::{Serialize, Serializer};
use std::error::Error as _;
struct MyStruct;
impl Serialize for MyStruct {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
@@ -813,7 +814,9 @@ mod tests {
let some_url = "https://google.com/";
let r = client.post(some_url);
let json_data = MyStruct;
assert!(r.json(&json_data).build().unwrap_err().is_serialization());
let err = r.json(&json_data).build().unwrap_err();
assert!(err.is_builder()); // well, duh ;)
assert!(err.source().unwrap().is::<serde_json::Error>());
}
#[test]

View File

@@ -205,8 +205,8 @@ impl Response {
/// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
pub fn json<T: DeserializeOwned>(self) -> crate::Result<T> {
wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e {
wait::Waited::TimedOut => crate::error::timedout(None),
wait::Waited::Executor(e) => crate::error::from(e),
wait::Waited::TimedOut(e) => crate::error::decode(e),
wait::Waited::Executor(e) => crate::error::decode(e),
wait::Waited::Inner(e) => e,
})
}
@@ -253,8 +253,8 @@ impl Response {
pub fn text_with_charset(self, default_encoding: &str) -> crate::Result<String> {
wait::timeout(self.inner.text_with_charset(default_encoding), self.timeout).map_err(|e| {
match e {
wait::Waited::TimedOut => crate::error::timedout(None),
wait::Waited::Executor(e) => crate::error::from(e),
wait::Waited::TimedOut(e) => crate::error::decode(e),
wait::Waited::Executor(e) => crate::error::decode(e),
wait::Waited::Inner(e) => e,
}
})
@@ -284,7 +284,7 @@ impl Response {
where
W: io::Write,
{
io::copy(self, w).map_err(crate::error::from)
io::copy(self, w).map_err(crate::error::response)
}
/// Turn a response into an error if the server returned an error.
@@ -359,8 +359,8 @@ impl Read for Response {
let timeout = self.timeout;
wait::timeout(self.body_mut().read(buf), timeout).map_err(|e| match e {
wait::Waited::TimedOut => crate::error::timedout(None).into_io(),
wait::Waited::Executor(e) => crate::error::from(e).into_io(),
wait::Waited::TimedOut(e) => crate::error::response(e).into_io(),
wait::Waited::Executor(e) => crate::error::response(e).into_io(),
wait::Waited::Inner(e) => e,
})
}

View File

@@ -7,14 +7,14 @@ use tokio::clock;
use tokio_executor::{
enter,
park::{Park, ParkThread, Unpark, UnparkThread},
EnterError,
};
pub(crate) fn timeout<F, I, E>(fut: F, timeout: Option<Duration>) -> Result<I, Waited<E>>
where
F: Future<Output = Result<I, E>>,
{
let _entered = enter().map_err(Waited::Executor)?;
let _entered =
enter().map_err(|_| Waited::Executor(crate::error::BlockingClientInAsyncContext))?;
let deadline = timeout.map(|d| {
log::trace!("wait at most {:?}", d);
clock::now() + d
@@ -39,7 +39,7 @@ where
let now = clock::now();
if now >= deadline {
log::trace!("wait timeout exceeded");
return Err(Waited::TimedOut);
return Err(Waited::TimedOut(crate::error::TimedOut));
}
log::trace!("park timeout {:?}", deadline - now);
@@ -53,8 +53,8 @@ where
#[derive(Debug)]
pub(crate) enum Waited<E> {
TimedOut,
Executor(EnterError),
TimedOut(crate::error::TimedOut),
Executor(crate::error::BlockingClientInAsyncContext),
Inner(E),
}