673 lines
19 KiB
Rust
673 lines
19 KiB
Rust
use std::error::Error as StdError;
|
|
use std::fmt;
|
|
use std::io;
|
|
|
|
use {StatusCode, Url};
|
|
|
|
/// The Errors that may occur when processing a `Request`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// extern crate serde;
|
|
/// extern crate reqwest;
|
|
///
|
|
/// use serde::Deserialize;
|
|
///
|
|
/// #[derive(Deserialize)]
|
|
/// struct Simple {
|
|
/// key: String
|
|
/// }
|
|
/// # fn main() { }
|
|
///
|
|
/// fn run() {
|
|
/// match make_request() {
|
|
/// Err(e) => handler(e),
|
|
/// Ok(_) => return,
|
|
/// }
|
|
/// }
|
|
/// // Response is not a json object conforming to the Simple struct
|
|
/// fn make_request() -> Result<Simple, reqwest::Error> {
|
|
/// reqwest::get("http://httpbin.org/ip")?.json()
|
|
/// }
|
|
///
|
|
/// fn handler(e: reqwest::Error) {
|
|
/// if e.is_http() {
|
|
/// match e.url() {
|
|
/// None => println!("No Url given"),
|
|
/// Some(url) => println!("Problem making request to: {}", url),
|
|
/// }
|
|
/// }
|
|
/// // Inspect the internal error and output it
|
|
/// if e.is_serialization() {
|
|
/// let serde_error = match e.get_ref() {
|
|
/// None => return,
|
|
/// Some(err) => err,
|
|
/// };
|
|
/// println!("problem parsing information {}", serde_error);
|
|
/// }
|
|
/// if e.is_redirect() {
|
|
/// println!("server redirecting too many times or making loop");
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
pub struct Error {
|
|
inner: Box<Inner>,
|
|
}
|
|
|
|
struct Inner {
|
|
kind: Kind,
|
|
url: Option<Url>,
|
|
}
|
|
|
|
|
|
/// A `Result` alias where the `Err` case is `reqwest::Error`.
|
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
|
|
|
impl Error {
|
|
fn new(kind: Kind, url: Option<Url>) -> Error {
|
|
Error {
|
|
inner: Box::new(Inner {
|
|
kind,
|
|
url,
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Returns a possible URL related to this error.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # fn run() {
|
|
/// // displays last stop of a redirect loop
|
|
/// let response = reqwest::get("http://site.with.redirect.loop");
|
|
/// if let Err(e) = response {
|
|
/// if e.is_redirect() {
|
|
/// if let Some(final_stop) = e.url() {
|
|
/// println!("redirect loop at {}", final_stop);
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// # }
|
|
/// ```
|
|
#[inline]
|
|
pub fn url(&self) -> Option<&Url> {
|
|
self.inner.url.as_ref()
|
|
}
|
|
|
|
pub(crate) fn with_url(mut self, url: Url) -> Error {
|
|
debug_assert_eq!(self.inner.url, None, "with_url overriding existing url");
|
|
self.inner.url = Some(url);
|
|
self
|
|
}
|
|
|
|
/// Returns a reference to the internal error, if available.
|
|
///
|
|
/// The `'static` bounds allows using `downcast_ref` to check the
|
|
/// details of the error.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// extern crate url;
|
|
/// # extern crate reqwest;
|
|
/// // retries requests with no host on localhost
|
|
/// # fn run() {
|
|
/// let invalid_request = "http://";
|
|
/// let mut response = reqwest::get(invalid_request);
|
|
/// if let Err(e) = response {
|
|
/// match e.get_ref().and_then(|e| e.downcast_ref::<url::ParseError>()) {
|
|
/// Some(&url::ParseError::EmptyHost) => {
|
|
/// let valid_request = format!("{}{}",invalid_request, "localhost");
|
|
/// response = reqwest::get(&valid_request);
|
|
/// },
|
|
/// _ => (),
|
|
/// }
|
|
/// }
|
|
/// # }
|
|
/// # fn main() {}
|
|
/// ```
|
|
#[inline]
|
|
pub fn get_ref(&self) -> Option<&(dyn StdError + Send + Sync + 'static)> {
|
|
match self.inner.kind {
|
|
Kind::Http(ref e) => Some(e),
|
|
Kind::Hyper(ref e) => Some(e),
|
|
Kind::Mime(ref e) => Some(e),
|
|
Kind::Url(ref e) => Some(e),
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
Kind::TlsIncompatible => None,
|
|
#[cfg(feature = "default-tls")]
|
|
Kind::NativeTls(ref e) => Some(e),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Kind::Rustls(ref e) => Some(e),
|
|
#[cfg(feature = "trust-dns")]
|
|
Kind::DnsSystemConf(ref e) => Some(e),
|
|
Kind::Io(ref e) => Some(e),
|
|
Kind::UrlEncoded(ref e) => Some(e),
|
|
Kind::Json(ref e) => Some(e),
|
|
Kind::UrlBadScheme |
|
|
Kind::TooManyRedirects |
|
|
Kind::RedirectLoop |
|
|
Kind::Status(_) |
|
|
Kind::UnknownProxyScheme |
|
|
Kind::Timer => None,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is related to HTTP.
|
|
#[inline]
|
|
pub fn is_http(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::Http(_) => true,
|
|
Kind::Hyper(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is related to a timeout.
|
|
pub fn is_timeout(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::Io(ref io) => io.kind() == io::ErrorKind::TimedOut,
|
|
Kind::Hyper(ref error) => {
|
|
error
|
|
.source()
|
|
.and_then(|cause| {
|
|
cause.downcast_ref::<io::Error>()
|
|
})
|
|
.map(|io| io.kind() == io::ErrorKind::TimedOut)
|
|
.unwrap_or(false)
|
|
},
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is serialization related.
|
|
#[inline]
|
|
pub fn is_serialization(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::Json(_) |
|
|
Kind::UrlEncoded(_) => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is from a `RedirectPolicy`.
|
|
#[inline]
|
|
pub fn is_redirect(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::TooManyRedirects |
|
|
Kind::RedirectLoop => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is from a request returning a 4xx error.
|
|
#[inline]
|
|
pub fn is_client_error(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::Status(code) => code.is_client_error(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns true if the error is from a request returning a 5xx error.
|
|
#[inline]
|
|
pub fn is_server_error(&self) -> bool {
|
|
match self.inner.kind {
|
|
Kind::Status(code) => code.is_server_error(),
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
/// Returns the status code, if the error was generated from a response.
|
|
#[inline]
|
|
pub fn status(&self) -> Option<StatusCode> {
|
|
match self.inner.kind {
|
|
Kind::Status(code) => Some(code),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
if let Some(ref url) = self.inner.url {
|
|
f.debug_tuple("Error")
|
|
.field(&self.inner.kind)
|
|
.field(url)
|
|
.finish()
|
|
} else {
|
|
f.debug_tuple("Error")
|
|
.field(&self.inner.kind)
|
|
.finish()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Error {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
if let Some(ref url) = self.inner.url {
|
|
try!(fmt::Display::fmt(url, f));
|
|
try!(f.write_str(": "));
|
|
}
|
|
match self.inner.kind {
|
|
Kind::Http(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::Hyper(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::Mime(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::Url(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::UrlBadScheme => f.write_str("URL scheme is not allowed"),
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
Kind::TlsIncompatible => f.write_str("Incompatible TLS identity type"),
|
|
#[cfg(feature = "default-tls")]
|
|
Kind::NativeTls(ref e) => fmt::Display::fmt(e, f),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Kind::Rustls(ref e) => fmt::Display::fmt(e, f),
|
|
#[cfg(feature = "trust-dns")]
|
|
Kind::DnsSystemConf(ref e) => {
|
|
write!(f, "failed to load DNS system conf: {}", e)
|
|
},
|
|
Kind::Io(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::UrlEncoded(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::Json(ref e) => fmt::Display::fmt(e, f),
|
|
Kind::TooManyRedirects => f.write_str("Too many redirects"),
|
|
Kind::RedirectLoop => f.write_str("Infinite redirect loop"),
|
|
Kind::Status(ref code) => {
|
|
let prefix = if code.is_client_error() {
|
|
"Client Error"
|
|
} else if code.is_server_error() {
|
|
"Server Error"
|
|
} else {
|
|
unreachable!("non-error status code: {:?}", code);
|
|
};
|
|
write!(f, "{}: {}", prefix, code)
|
|
}
|
|
Kind::UnknownProxyScheme => f.write_str("Unknown proxy scheme"),
|
|
Kind::Timer => f.write_str("timer unavailable"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl StdError for Error {
|
|
fn description(&self) -> &str {
|
|
match self.inner.kind {
|
|
Kind::Http(ref e) => e.description(),
|
|
Kind::Hyper(ref e) => e.description(),
|
|
Kind::Mime(ref e) => e.description(),
|
|
Kind::Url(ref e) => e.description(),
|
|
Kind::UrlBadScheme => "URL scheme is not allowed",
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
Kind::TlsIncompatible => "Incompatible TLS identity type",
|
|
#[cfg(feature = "default-tls")]
|
|
Kind::NativeTls(ref e) => e.description(),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Kind::Rustls(ref e) => e.description(),
|
|
#[cfg(feature = "trust-dns")]
|
|
Kind::DnsSystemConf(_) => "failed to load DNS system conf",
|
|
Kind::Io(ref e) => e.description(),
|
|
Kind::UrlEncoded(ref e) => e.description(),
|
|
Kind::Json(ref e) => e.description(),
|
|
Kind::TooManyRedirects => "Too many redirects",
|
|
Kind::RedirectLoop => "Infinite redirect loop",
|
|
Kind::Status(code) => {
|
|
if code.is_client_error() {
|
|
"Client Error"
|
|
} else if code.is_server_error() {
|
|
"Server Error"
|
|
} else {
|
|
unreachable!("non-error status code: {:?}", code);
|
|
}
|
|
}
|
|
Kind::UnknownProxyScheme => "Unknown proxy scheme",
|
|
Kind::Timer => "timer unavailable",
|
|
}
|
|
}
|
|
|
|
// Keep this for now, as std::io::Error didn't support source() until 1.35
|
|
#[allow(deprecated)]
|
|
fn cause(&self) -> Option<&dyn StdError> {
|
|
match self.inner.kind {
|
|
Kind::Http(ref e) => e.cause(),
|
|
Kind::Hyper(ref e) => e.cause(),
|
|
Kind::Mime(ref e) => e.cause(),
|
|
Kind::Url(ref e) => e.cause(),
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
Kind::TlsIncompatible => None,
|
|
#[cfg(feature = "default-tls")]
|
|
Kind::NativeTls(ref e) => e.cause(),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Kind::Rustls(ref e) => e.cause(),
|
|
#[cfg(feature = "trust-dns")]
|
|
Kind::DnsSystemConf(ref e) => e.cause(),
|
|
Kind::Io(ref e) => e.cause(),
|
|
Kind::UrlEncoded(ref e) => e.cause(),
|
|
Kind::Json(ref e) => e.cause(),
|
|
Kind::UrlBadScheme |
|
|
Kind::TooManyRedirects |
|
|
Kind::RedirectLoop |
|
|
Kind::Status(_) |
|
|
Kind::UnknownProxyScheme |
|
|
Kind::Timer => None,
|
|
}
|
|
}
|
|
|
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
|
match self.inner.kind {
|
|
Kind::Http(ref e) => e.source(),
|
|
Kind::Hyper(ref e) => e.source(),
|
|
Kind::Mime(ref e) => e.source(),
|
|
Kind::Url(ref e) => e.source(),
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
Kind::TlsIncompatible => None,
|
|
#[cfg(feature = "default-tls")]
|
|
Kind::NativeTls(ref e) => e.source(),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Kind::Rustls(ref e) => e.source(),
|
|
#[cfg(feature = "trust-dns")]
|
|
Kind::DnsSystemConf(ref e) => e.source(),
|
|
Kind::Io(ref e) => e.source(),
|
|
Kind::UrlEncoded(ref e) => e.source(),
|
|
Kind::Json(ref e) => e.source(),
|
|
Kind::UrlBadScheme |
|
|
Kind::TooManyRedirects |
|
|
Kind::RedirectLoop |
|
|
Kind::Status(_) |
|
|
Kind::UnknownProxyScheme |
|
|
Kind::Timer => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) enum Kind {
|
|
Http(::http::Error),
|
|
Hyper(::hyper::Error),
|
|
Mime(::mime::FromStrError),
|
|
Url(::url::ParseError),
|
|
UrlBadScheme,
|
|
#[cfg(all(feature = "default-tls", feature = "rustls-tls"))]
|
|
TlsIncompatible,
|
|
#[cfg(feature = "default-tls")]
|
|
NativeTls(::native_tls::Error),
|
|
#[cfg(feature = "rustls-tls")]
|
|
Rustls(::rustls::TLSError),
|
|
#[cfg(feature = "trust-dns")]
|
|
DnsSystemConf(io::Error),
|
|
Io(io::Error),
|
|
UrlEncoded(::serde_urlencoded::ser::Error),
|
|
Json(::serde_json::Error),
|
|
TooManyRedirects,
|
|
RedirectLoop,
|
|
Status(StatusCode),
|
|
UnknownProxyScheme,
|
|
Timer,
|
|
}
|
|
|
|
|
|
impl From<::http::Error> for Kind {
|
|
#[inline]
|
|
fn from(err: ::http::Error) -> Kind {
|
|
Kind::Http(err)
|
|
}
|
|
}
|
|
|
|
impl From<::hyper::Error> for Kind {
|
|
#[inline]
|
|
fn from(err: ::hyper::Error) -> Kind {
|
|
Kind::Hyper(err)
|
|
}
|
|
}
|
|
|
|
impl From<::mime::FromStrError> for Kind {
|
|
#[inline]
|
|
fn from(err: ::mime::FromStrError) -> Kind {
|
|
Kind::Mime(err)
|
|
}
|
|
}
|
|
|
|
impl From<io::Error> for Kind {
|
|
#[inline]
|
|
fn from(err: io::Error) -> Kind {
|
|
Kind::Io(err)
|
|
}
|
|
}
|
|
|
|
impl From<::url::ParseError> for Kind {
|
|
#[inline]
|
|
fn from(err: ::url::ParseError) -> Kind {
|
|
Kind::Url(err)
|
|
}
|
|
}
|
|
|
|
impl From<::serde_urlencoded::ser::Error> for Kind {
|
|
#[inline]
|
|
fn from(err: ::serde_urlencoded::ser::Error) -> Kind {
|
|
Kind::UrlEncoded(err)
|
|
}
|
|
}
|
|
|
|
impl From<::serde_json::Error> for Kind {
|
|
#[inline]
|
|
fn from(err: ::serde_json::Error) -> Kind {
|
|
Kind::Json(err)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "default-tls")]
|
|
impl From<::native_tls::Error> for Kind {
|
|
fn from(err: ::native_tls::Error) -> Kind {
|
|
Kind::NativeTls(err)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "rustls-tls")]
|
|
impl From<::rustls::TLSError> for Kind {
|
|
fn from(err: ::rustls::TLSError) -> Kind {
|
|
Kind::Rustls(err)
|
|
}
|
|
}
|
|
|
|
impl<T> From<::wait::Waited<T>> for Kind
|
|
where T: Into<Kind> {
|
|
fn from(err: ::wait::Waited<T>) -> Kind {
|
|
match err {
|
|
::wait::Waited::TimedOut => io_timeout().into(),
|
|
::wait::Waited::Err(e) => e.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<::tokio::timer::Error> for Kind {
|
|
fn from(_err: ::tokio::timer::Error) -> Kind {
|
|
Kind::Timer
|
|
}
|
|
}
|
|
|
|
fn io_timeout() -> io::Error {
|
|
io::Error::new(io::ErrorKind::TimedOut, "timed out")
|
|
}
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
pub(crate) struct InternalFrom<T>(pub T, pub Option<Url>);
|
|
|
|
#[doc(hidden)] // https://github.com/rust-lang/rust/issues/42323
|
|
impl From<InternalFrom<Error>> for Error {
|
|
#[inline]
|
|
fn from(other: InternalFrom<Error>) -> Error {
|
|
other.0
|
|
}
|
|
}
|
|
|
|
#[doc(hidden)] // https://github.com/rust-lang/rust/issues/42323
|
|
impl<T> From<InternalFrom<T>> for Error
|
|
where
|
|
T: Into<Kind>,
|
|
{
|
|
#[inline]
|
|
fn from(other: InternalFrom<T>) -> Error {
|
|
Error::new(other.0.into(), other.1)
|
|
}
|
|
}
|
|
|
|
pub(crate) fn from<T>(err: T) -> Error
|
|
where
|
|
T: Into<Kind>,
|
|
{
|
|
InternalFrom(err, None).into()
|
|
}
|
|
|
|
pub(crate) fn into_io(e: Error) -> io::Error {
|
|
match e.inner.kind {
|
|
Kind::Io(io) => io,
|
|
_ => io::Error::new(io::ErrorKind::Other, e),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn from_io(e: io::Error) -> Error {
|
|
if e.get_ref().map(|r| r.is::<Error>()).unwrap_or(false) {
|
|
*e
|
|
.into_inner()
|
|
.expect("io::Error::get_ref was Some(_)")
|
|
.downcast::<Error>()
|
|
.expect("StdError::is() was true")
|
|
} else {
|
|
from(e)
|
|
}
|
|
}
|
|
|
|
|
|
macro_rules! try_ {
|
|
($e:expr) => (
|
|
match $e {
|
|
Ok(v) => v,
|
|
Err(err) => {
|
|
return Err(::error::from(err));
|
|
}
|
|
}
|
|
);
|
|
($e:expr, $url:expr) => (
|
|
match $e {
|
|
Ok(v) => v,
|
|
Err(err) => {
|
|
return Err(::Error::from(::error::InternalFrom(err, Some($url.clone()))));
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
macro_rules! try_io {
|
|
($e:expr) => (
|
|
match $e {
|
|
Ok(v) => v,
|
|
Err(ref err) if err.kind() == ::std::io::ErrorKind::WouldBlock => {
|
|
return Ok(::futures::Async::NotReady);
|
|
}
|
|
Err(err) => {
|
|
return Err(::error::from_io(err));
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
pub(crate) fn loop_detected(url: Url) -> Error {
|
|
Error::new(Kind::RedirectLoop, Some(url))
|
|
}
|
|
|
|
pub(crate) fn too_many_redirects(url: Url) -> Error {
|
|
Error::new(Kind::TooManyRedirects, Some(url))
|
|
}
|
|
|
|
pub(crate) fn timedout(url: Option<Url>) -> Error {
|
|
Error::new(Kind::Io(io_timeout()), url)
|
|
}
|
|
|
|
pub(crate) fn status_code(url: Url, status: StatusCode) -> Error {
|
|
Error::new(Kind::Status(status), Some(url))
|
|
}
|
|
|
|
pub(crate) fn url_bad_scheme(url: Url) -> Error {
|
|
Error::new(Kind::UrlBadScheme, Some(url))
|
|
}
|
|
|
|
#[cfg(feature = "trust-dns")]
|
|
pub(crate) fn dns_system_conf(io: io::Error) -> Error {
|
|
Error::new(Kind::DnsSystemConf(io), None)
|
|
}
|
|
|
|
pub(crate) fn unknown_proxy_scheme() -> Error {
|
|
Error::new(Kind::UnknownProxyScheme, None)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[allow(deprecated)]
|
|
#[test]
|
|
fn test_cause_chain() {
|
|
#[derive(Debug)]
|
|
struct Chain<T>(Option<T>);
|
|
|
|
impl<T: fmt::Display> fmt::Display for Chain<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
if let Some(ref link) = self.0 {
|
|
write!(f, "chain: {}", link)
|
|
} else {
|
|
f.write_str("root")
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: StdError> StdError for Chain<T> {
|
|
fn description(&self) -> &str {
|
|
if self.0.is_some() {
|
|
"chain"
|
|
} else {
|
|
"root"
|
|
}
|
|
}
|
|
fn cause(&self) -> Option<&StdError> {
|
|
if let Some(ref e) = self.0 {
|
|
Some(e)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
let root = Chain(None::<Error>);
|
|
let io = ::std::io::Error::new(::std::io::ErrorKind::Other, root);
|
|
let err = Error::new(Kind::Io(io), None);
|
|
assert!(err.cause().is_none());
|
|
assert_eq!(err.to_string(), "root");
|
|
|
|
|
|
let root = ::std::io::Error::new(::std::io::ErrorKind::Other, Chain(None::<Error>));
|
|
let link = Chain(Some(root));
|
|
let io = ::std::io::Error::new(::std::io::ErrorKind::Other, link);
|
|
let err = Error::new(Kind::Io(io), None);
|
|
assert!(err.cause().is_some());
|
|
assert_eq!(err.to_string(), "chain: root");
|
|
}
|
|
|
|
#[test]
|
|
fn mem_size_of() {
|
|
use std::mem::size_of;
|
|
assert_eq!(size_of::<Error>(), size_of::<usize>());
|
|
}
|
|
|
|
#[test]
|
|
fn roundtrip_io_error() {
|
|
let orig = unknown_proxy_scheme();
|
|
// Convert reqwest::Error into an io::Error...
|
|
let io = into_io(orig);
|
|
// Convert that io::Error back into a reqwest::Error...
|
|
let err = from_io(io);
|
|
// It should have pulled out the original, not nested it...
|
|
match err.inner.kind {
|
|
Kind::UnknownProxyScheme => (),
|
|
_ => panic!("{:?}", err),
|
|
}
|
|
}
|
|
}
|