diff --git a/Cargo.toml b/Cargo.toml index 0c1e3f8..4af19d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ serde = "1.0.97" serde_json = "1.0.40" serde_urlencoded = "0.6.1" url = "2.0.0" +anyhow = "1.0.23" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # encoding diff --git a/README.md b/README.md index 2fb829c..5cf5fd0 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ quick script, or a cross-platform SDK, Surf will make it work. ```rust use async_std::task; -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { task::block_on(async { let mut res = surf::get("https://httpbin.org/get").await?; dbg!(res.body_string().await?); @@ -79,7 +79,7 @@ type directly. ```rust use async_std::task; -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { task::block_on(async { dbg!(surf::get("https://httpbin.org/get").recv_string().await?); Ok(()) @@ -93,7 +93,7 @@ Both sending and receiving JSON is real easy too. use async_std::task; use serde::{Deserialize, Serialize}; -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { #[derive(Deserialize, Serialize)] struct Ip { ip: String @@ -118,7 +118,7 @@ And even creating streaming proxies is no trouble at all. ```rust use async_std::task; -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { task::block_on(async { let reader = surf::get("https://img.fyi/q6YvNqP").await?; let res = surf::post("https://box.rs/upload").body(reader).await?; @@ -129,11 +129,11 @@ fn main() -> Result<(), Box> { ## Installation -Install OpenSSL - +Install OpenSSL - - Ubuntu - ``` sudo apt install libssl-dev ``` - Fedora - ``` sudo dnf install openssl-devel ``` -Make sure your rust is up to date using: +Make sure your rust is up to date using: ``` rustup update ``` With [cargo add](https://github.com/killercup/cargo-edit#Installation) installed : diff --git a/examples/hello_world.rs b/examples/hello_world.rs index f99dbf6..e87e2bd 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,14 +1,16 @@ use async_std::task; +type BoxError = Box; + // The need for Ok with turbofish is explained here // https://rust-lang.github.io/async-book/07_workarounds/03_err_in_async_blocks.html -fn main() -> Result<(), surf::Exception> { +fn main() -> Result<(), BoxError> { femme::start(log::LevelFilter::Info)?; task::block_on(async { let uri = "https://httpbin.org/get"; let string: String = surf::get(uri).recv_string().await?; println!("{}", string); - Ok::<(), surf::Exception>(()) + Ok(()) }) } diff --git a/examples/middleware.rs b/examples/middleware.rs index e5b6717..2941354 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -4,13 +4,15 @@ use surf::middleware::{HttpClient, Middleware, Next, Request, Response}; struct Printer; +type BoxError = Box; + impl Middleware for Printer { fn handle<'a>( &'a self, req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { println!("sending a request!"); let res = next.run(req, client).await?; @@ -20,15 +22,13 @@ impl Middleware for Printer { } } -// The need for Ok with turbofish is explained here -// https://rust-lang.github.io/async-book/07_workarounds/03_err_in_async_blocks.html -fn main() -> Result<(), surf::Exception> { +fn main() -> Result<(), BoxError> { femme::start(log::LevelFilter::Info)?; task::block_on(async { surf::get("https://httpbin.org/get") - .middleware(Printer {}) + .middleware(Printer) .await?; - Ok::<(), surf::Exception>(()) + Ok(()) }) } diff --git a/examples/next_reuse.rs b/examples/next_reuse.rs index 11d7c21..a834536 100644 --- a/examples/next_reuse.rs +++ b/examples/next_reuse.rs @@ -5,13 +5,15 @@ use surf::middleware::{Body, HttpClient, Middleware, Next, Request, Response}; struct Doubler; +type BoxError = Box; + impl Middleware for Doubler { fn handle<'a>( &'a self, req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { if req.method().is_safe() { let mut new_req = Request::new(Body::empty()); *new_req.method_mut() = req.method().clone(); @@ -36,15 +38,17 @@ impl Middleware for Doubler { Ok(res) }) } else { - next.run(req, client) + Box::pin(async move { + next.run(req, client).await.map_err(From::from) + }) } } } // The need for Ok with turbofish is explained here // https://rust-lang.github.io/async-book/07_workarounds/03_err_in_async_blocks.html -fn main() -> Result<(), surf::Exception> { - femme::start(log::LevelFilter::Info).unwrap(); +fn main() -> Result<(), BoxError> { + femme::start(log::LevelFilter::Info)?; task::block_on(async { let mut res = surf::get("https://httpbin.org/get") .middleware(Doubler {}) @@ -53,6 +57,6 @@ fn main() -> Result<(), surf::Exception> { let body = res.body_bytes().await?; let body = String::from_utf8_lossy(&body); println!("{}", body); - Ok::<(), surf::Exception>(()) + Ok(()) }) } diff --git a/examples/persistent.rs b/examples/persistent.rs index 8b59e40..62d25df 100644 --- a/examples/persistent.rs +++ b/examples/persistent.rs @@ -1,14 +1,16 @@ use async_std::task; +type BoxError = Box; + // The need for Ok with turbofish is explained here // https://rust-lang.github.io/async-book/07_workarounds/03_err_in_async_blocks.html -fn main() -> Result<(), surf::Exception> { - femme::start(log::LevelFilter::Info).unwrap(); +fn main() -> Result<(), BoxError> { + femme::start(log::LevelFilter::Info)?; task::block_on(async { let client = surf::Client::new(); let req1 = client.get("https://httpbin.org/get").recv_string(); let req2 = client.get("https://httpbin.org/get").recv_string(); futures::future::try_join(req1, req2).await?; - Ok::<(), surf::Exception>(()) + Ok(()) }) } diff --git a/examples/post.rs b/examples/post.rs index c3b690d..2dc09fb 100644 --- a/examples/post.rs +++ b/examples/post.rs @@ -1,14 +1,15 @@ use async_std::task; -// The need for Ok with turbofish is explained here -// https://rust-lang.github.io/async-book/07_workarounds/03_err_in_async_blocks.html -fn main() -> Result<(), surf::Exception> { - femme::start(log::LevelFilter::Info).unwrap(); +type BoxError = Box; + +fn main() -> Result<(), BoxError> { + femme::start(log::LevelFilter::Info)?; task::block_on(async { let uri = "https://httpbin.org/post"; let data = serde_json::json!({ "name": "chashu" }); + // unwrap note: we are definitely passing valid json so we should never panic. let res = surf::post(uri).body_json(&data).unwrap().await?; assert_eq!(res.status(), 200); - Ok::<(), surf::Exception>(()) + Ok(()) }) } diff --git a/src/client.rs b/src/client.rs index 48b43e5..10fe4be 100644 --- a/src/client.rs +++ b/src/client.rs @@ -9,8 +9,9 @@ use super::http_client::native::NativeClient; /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let req1 = client.get("https://httpbin.org/get").recv_string(); /// let req2 = client.get("https://httpbin.org/get").recv_string(); @@ -29,8 +30,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// # Ok(()) } /// ``` @@ -62,8 +64,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.get("https://httpbin.org/get").recv_string().await?; /// # Ok(()) } @@ -86,8 +89,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.head("https://httpbin.org/head").recv_string().await?; /// # Ok(()) } @@ -110,8 +114,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.post("https://httpbin.org/post").recv_string().await?; /// # Ok(()) } @@ -134,8 +139,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.put("https://httpbin.org/put").recv_string().await?; /// # Ok(()) } @@ -158,8 +164,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.delete("https://httpbin.org/delete").recv_string().await?; /// # Ok(()) } @@ -182,8 +189,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.connect("https://httpbin.org/connect").recv_string().await?; /// # Ok(()) } @@ -206,8 +214,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.options("https://httpbin.org/options").recv_string().await?; /// # Ok(()) } @@ -230,8 +239,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.trace("https://httpbin.org/trace").recv_string().await?; /// # Ok(()) } @@ -254,8 +264,9 @@ impl Client { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let client = surf::Client::new(); /// let string = client.patch("https://httpbin.org/patch").recv_string().await?; /// # Ok(()) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..9592631 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,61 @@ +//! The error types for `surf`. +//! +//! This module contains the type `Error` which represents any error that surf may return. It also +//! contains a `Result` type alias which is like `std::result::Result` but defaults to using +//! `surf::Error`, and the `BoxError` type alias that is just shorthand for a boxed error. `Error` +//! and `Result` are re-exported at the crate root, so you shouldn't usually have to use anything +//! in this module. +use std::{ + fmt::{self, Display, Debug}, +}; + +/// A version of `std::result::Result` that defaults the error type to `surf::Error`. +pub type Result = std::result::Result; + +/// A generic error type. +pub struct Error(pub(crate) anyhow::Error); + +impl Error { + /// Use this when you need to implement middleware, where the error type must be `surf::Error`. + pub(crate) fn new(error: E) -> Self + where E: std::error::Error + Send + Sync + 'static + { + Self(anyhow::Error::new(error)) + } + + /// Use this to create string errors. + pub(crate) fn msg(message: M) -> Self + where M: Display + Debug + Send + Sync + 'static + { + Self(anyhow::Error::msg(message)) + } + + /// Use this to add context to errors + #[allow(dead_code)] + pub(crate) fn context(self, context: C) -> Self + where C: Display + Send + Sync + 'static + { + Self(anyhow::Error::context(self.0, context)) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + +/// A type alias for any boxed error. +pub type BoxError = Box; diff --git a/src/http_client/hyper.rs b/src/http_client/hyper.rs index 4c14598..9010561 100644 --- a/src/http_client/hyper.rs +++ b/src/http_client/hyper.rs @@ -17,6 +17,7 @@ use std::sync::Arc; use std::task::{Context, Poll}; use super::{Body, HttpClient, Request, Response}; +use crate::error::BoxError; /// Hyper HTTP Client. #[derive(Debug)] @@ -91,7 +92,7 @@ struct ChunkStream { } impl futures::Stream for ChunkStream { - type Item = Result>; + type Item = Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // This is not at all efficient, but that's okay for now. diff --git a/src/lib.rs b/src/lib.rs index 3fcc9df..0a67364 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,9 @@ //! //! # Examples //! ```no_run +//! # use surf::error::BoxError; //! # #[async_std::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let mut res = surf::get("https://httpbin.org/get").await?; //! dbg!(res.body_string().await?); //! # Ok(()) } @@ -23,17 +24,19 @@ //! //! It's also possible to skip the intermediate `Response`, and access the response type directly. //! ```no_run +//! # use surf::error::BoxError; //! # #[async_std::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! dbg!(surf::get("https://httpbin.org/get").recv_string().await?); //! # Ok(()) } //! ``` //! //! Both sending and receiving JSON is real easy too. //! ```no_run +//! # use surf::error::BoxError; //! # use serde::{Deserialize, Serialize}; //! # #[async_std::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! #[derive(Deserialize, Serialize)] //! struct Ip { //! ip: String @@ -53,8 +56,9 @@ //! And even creating streaming proxies is no trouble at all. //! //! ```no_run +//! # use surf::error::BoxError; //! # #[async_std::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let reader = surf::get("https://img.fyi/q6YvNqP").await?; //! let res = surf::post("https://box.rs/upload").body(reader).await?; //! # Ok(()) } @@ -75,6 +79,8 @@ #![cfg_attr(test, deny(warnings))] mod client; +#[doc(hidden)] +pub mod error; mod http_client; mod request; mod response; @@ -87,6 +93,7 @@ pub use mime; pub use url; pub use client::Client; +pub use error::{Result, Error}; pub use request::Request; pub use response::{DecodeError, Response}; @@ -95,5 +102,3 @@ mod one_off; #[cfg(feature = "native-client")] pub use one_off::{connect, delete, get, head, options, patch, post, put, trace}; -/// A generic error type. -pub type Exception = Box; diff --git a/src/middleware/logger/mod.rs b/src/middleware/logger/mod.rs index aac9318..f05cc70 100644 --- a/src/middleware/logger/mod.rs +++ b/src/middleware/logger/mod.rs @@ -3,8 +3,9 @@ //! # Examples //! //! ``` +//! # use surf::error::BoxError; //! # #[async_std::main] -//! # async fn main() -> Result<(), Box> { +//! # async fn main() -> Result<(), BoxError> { //! let mut res = surf::get("https://httpbin.org/get") //! .middleware(surf::middleware::logger::new()) //! .await?; @@ -29,8 +30,9 @@ use native::Logger; /// # Examples /// /// ``` +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let mut res = surf::get("https://httpbin.org/get") /// .middleware(surf::middleware::logger::new()) /// .await?; diff --git a/src/middleware/logger/native.rs b/src/middleware/logger/native.rs index 325de79..f048b13 100644 --- a/src/middleware/logger/native.rs +++ b/src/middleware/logger/native.rs @@ -1,5 +1,5 @@ use crate::http_client::HttpClient; -use crate::middleware::{Middleware, Next, Request, Response}; +use crate::middleware::{Middleware, Next, Request, Response, BoxError}; use futures::future::BoxFuture; use std::fmt::Arguments; @@ -28,7 +28,7 @@ impl Middleware for Logger { req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { let start_time = time::Instant::now(); let uri = format!("{}", req.uri()); diff --git a/src/middleware/logger/wasm.rs b/src/middleware/logger/wasm.rs index 225208e..e194676 100644 --- a/src/middleware/logger/wasm.rs +++ b/src/middleware/logger/wasm.rs @@ -25,7 +25,7 @@ impl Middleware for Logger { req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { Box::pin(async move { let uri = format!("{}", req.uri()); let method = format!("{}", req.method()); diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 0341059..1e58f8b 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -16,7 +16,7 @@ //! req: Request, //! client: C, //! next: Next<'a, C>, -//! ) -> BoxFuture<'a, Result> { +//! ) -> BoxFuture<'a, Result>> { //! Box::pin(async move { //! println!("sending request to {}", req.uri()); //! let now = time::Instant::now(); @@ -35,7 +35,7 @@ //! use surf::middleware::{Next, Middleware, Request, Response, HttpClient}; //! use std::time; //! -//! fn logger<'a, C: HttpClient>(req: Request, client: C, next: Next<'a, C>) -> BoxFuture<'a, Result> { +//! fn logger<'a, C: HttpClient>(req: Request, client: C, next: Next<'a, C>) -> BoxFuture<'a, Result> { //! Box::pin(async move { //! println!("sending request to {}", req.uri()); //! let now = time::Instant::now(); @@ -51,7 +51,7 @@ pub use crate::http_client::{Body, HttpClient, Request, Response}; pub mod logger; -use crate::Exception; +use crate::error::{Error, BoxError}; use futures::future::BoxFuture; use std::sync::Arc; @@ -63,7 +63,7 @@ pub trait Middleware: 'static + Send + Sync { req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result>; + ) -> BoxFuture<'a, Result>; } // This allows functions to work as middleware too. @@ -72,14 +72,14 @@ where F: Send + Sync + 'static - + for<'a> Fn(Request, C, Next<'a, C>) -> BoxFuture<'a, Result>, + + for<'a> Fn(Request, C, Next<'a, C>) -> BoxFuture<'a, Result>, { fn handle<'a>( &'a self, req: Request, client: C, next: Next<'a, C>, - ) -> BoxFuture<'a, Result> { + ) -> BoxFuture<'a, Result> { (self)(req, client, next) } } @@ -88,7 +88,7 @@ where #[allow(missing_debug_implementations)] pub struct Next<'a, C: HttpClient> { next_middleware: &'a [Arc>], - endpoint: &'a (dyn (Fn(Request, C) -> BoxFuture<'static, Result>) + endpoint: &'a (dyn (Fn(Request, C) -> BoxFuture<'static, Result>) + 'static + Send + Sync), @@ -109,7 +109,7 @@ impl<'a, C: HttpClient> Next<'a, C> { /// Create a new instance pub fn new( next: &'a [Arc>], - endpoint: &'a (dyn (Fn(Request, C) -> BoxFuture<'static, Result>) + endpoint: &'a (dyn (Fn(Request, C) -> BoxFuture<'static, Result>) + 'static + Send + Sync), @@ -121,10 +121,12 @@ impl<'a, C: HttpClient> Next<'a, C> { } /// Asynchronously execute the remaining middleware chain. - pub fn run(mut self, req: Request, client: C) -> BoxFuture<'a, Result> { + pub fn run(mut self, req: Request, client: C) -> BoxFuture<'a, Result> { if let Some((current, next)) = self.next_middleware.split_first() { self.next_middleware = next; - current.handle(req, client, self) + Box::pin(async move { + current.handle(req, client, self).await.map_err(|e| Error(anyhow::anyhow!(e))) + }) } else { (self.endpoint)(req, client) } diff --git a/src/one_off.rs b/src/one_off.rs index ca4513d..560c507 100644 --- a/src/one_off.rs +++ b/src/one_off.rs @@ -25,8 +25,9 @@ use super::Request; /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::get("https://httpbin.org/get").recv_string().await?; /// # Ok(()) } /// ``` @@ -66,8 +67,9 @@ pub fn get(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::head("https://httpbin.org/head").recv_string().await?; /// # Ok(()) } /// ``` @@ -124,8 +126,9 @@ pub fn head(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::post("https://httpbin.org/post").recv_string().await?; /// # Ok(()) } /// ``` @@ -160,8 +163,9 @@ pub fn post(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::put("https://httpbin.org/put").recv_string().await?; /// # Ok(()) } /// ``` @@ -191,8 +195,9 @@ pub fn put(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::delete("https://httpbin.org/delete").recv_string().await?; /// # Ok(()) } /// ``` @@ -231,8 +236,9 @@ pub fn delete(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::connect("https://httpbin.org/connect").recv_string().await?; /// # Ok(()) } /// ``` @@ -264,8 +270,9 @@ pub fn connect(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::options("https://httpbin.org/options").recv_string().await?; /// # Ok(()) } /// ``` @@ -301,8 +308,9 @@ pub fn options(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::trace("https://httpbin.org/trace").recv_string().await?; /// # Ok(()) } /// ``` @@ -344,8 +352,9 @@ pub fn trace(uri: impl AsRef) -> Request { /// # Examples /// /// ```no_run +/// # use surf::error::BoxError; /// # #[async_std::main] -/// # async fn main() -> Result<(), Box> { +/// # async fn main() -> Result<(), BoxError> { /// let string = surf::patch("https://httpbin.org/patch").recv_string().await?; /// # Ok(()) } /// ``` diff --git a/src/request.rs b/src/request.rs index 9517962..1d92919 100644 --- a/src/request.rs +++ b/src/request.rs @@ -8,7 +8,7 @@ use url::Url; use crate::headers::Headers; use crate::http_client::{self, Body, HttpClient}; use crate::middleware::{Middleware, Next}; -use crate::Exception; +use crate::Error; use crate::Response; use std::fmt; @@ -35,7 +35,7 @@ pub struct Request { /// Holds the inner middleware. middleware: Option>>>, /// Holds the state of the `impl Future`. - fut: Option>>, + fut: Option>>, /// Holds a reference to the Url url: Url, } @@ -51,8 +51,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::{http, url}; /// /// let method = http::Method::GET; @@ -97,8 +98,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let res = surf::get("https://httpbin.org/get") /// .middleware(surf::middleware::logger::new()) /// .await?; @@ -114,9 +116,10 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Serialize, Deserialize)] /// struct Index { /// page: u32 @@ -127,13 +130,12 @@ impl Request { /// assert_eq!(page, 2); /// # Ok(()) } /// ``` - pub fn query(&self) -> Result { - use std::io::{Error, ErrorKind}; + pub fn query(&self) -> Result { let query = self .url .query() - .ok_or_else(|| Error::from(ErrorKind::InvalidData))?; - Ok(serde_urlencoded::from_str(query)?) + .ok_or_else(|| Error::msg("no query found on url"))?; + Ok(serde_urlencoded::from_str(query).map_err(Error::new)?) } /// Set the URL querystring. @@ -141,9 +143,10 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Serialize, Deserialize)] /// struct Index { /// page: u32 @@ -174,8 +177,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let req = surf::get("https://httpbin.org/get") /// .set_header("X-Requested-With", "surf"); /// assert_eq!(req.header("X-Requested-With"), Some("surf")); @@ -191,8 +195,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let req = surf::get("https://httpbin.org/get") /// .set_header("X-Requested-With", "surf"); /// assert_eq!(req.header("X-Requested-With"), Some("surf")); @@ -211,7 +216,7 @@ impl Request { /// /// ```no_run /// # #[async_std::main] - /// # async fn main() -> Result<(), surf::Exception> { + /// # async fn main() -> Result<(), surf::Error> { /// let mut req = surf::get("https://httpbin.org/get") /// .set_header("X-Requested-With", "surf"); /// @@ -229,8 +234,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::http; /// let req = surf::get("https://httpbin.org/get"); /// assert_eq!(req.method(), http::Method::GET); @@ -246,8 +252,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::url::Url; /// let req = surf::get("https://httpbin.org/get"); /// assert_eq!(req.url(), &Url::parse("https://httpbin.org/get")?); @@ -273,8 +280,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::mime; /// let req = surf::post("https://httpbin.org/get") /// .set_mime(mime::TEXT_CSS); @@ -293,8 +301,9 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::mime; /// let req = surf::post("https://httpbin.org/get") /// .set_mime(mime::TEXT_CSS); @@ -314,8 +323,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let reader = surf::get("https://httpbin.org/get").await?; /// let uri = "https://httpbin.org/post"; /// let res = surf::post(uri).body(reader).await?; @@ -343,8 +353,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let uri = "https://httpbin.org/post"; /// let data = serde_json::json!({ "name": "chashu" }); /// let res = surf::post(uri).body_json(&data)?.await?; @@ -365,8 +376,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let uri = "https://httpbin.org/post"; /// let data = "hello world".to_string(); /// let res = surf::post(uri).body_string(data).await?; @@ -387,8 +399,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let uri = "https://httpbin.org/post"; /// let data = b"hello world"; /// let res = surf::post(uri).body_bytes(data).await?; @@ -417,8 +430,9 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), surf::Exception> { + /// # async fn main() -> Result<(), BoxError> { /// let res = surf::post("https://httpbin.org/post") /// .body_file("README.md")? /// .await?; @@ -445,9 +459,10 @@ impl Request { /// # Examples /// /// ``` + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Serialize, Deserialize)] /// struct Body { /// apples: u32 @@ -474,15 +489,16 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let bytes = surf::get("https://httpbin.org/get").recv_bytes().await?; /// assert!(bytes.len() > 0); /// # Ok(()) } /// ``` - pub async fn recv_bytes(self) -> Result, Exception> { - let mut req = self.await?; - Ok(req.body_bytes().await?) + pub async fn recv_bytes(self) -> Result, Error> { + let mut req = self.await.map_err(Error::new)?; + Ok(req.body_bytes().await.map_err(Error::new)?) } /// Submit the request and get the response body as a string. @@ -490,13 +506,14 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let string = surf::get("https://httpbin.org/get").recv_string().await?; /// assert!(string.len() > 0); /// # Ok(()) } /// ``` - pub async fn recv_string(self) -> Result { + pub async fn recv_string(self) -> Result { let mut req = self.await?; Ok(req.body_string().await?) } @@ -506,9 +523,10 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Deserialize, Serialize)] /// struct Ip { /// ip: String @@ -519,9 +537,9 @@ impl Request { /// assert!(ip.len() > 10); /// # Ok(()) } /// ``` - pub async fn recv_json(self) -> Result { - let mut req = self.await?; - Ok(req.body_json::().await?) + pub async fn recv_json(self) -> Result { + let mut req = self.await.map_err(Error::new)?; + Ok(req.body_json::().await.map_err(Error::new)?) } /// Submit the request and decode the response body from form encoding into a struct. @@ -537,9 +555,10 @@ impl Request { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Deserialize, Serialize)] /// struct Body { /// apples: u32 @@ -549,7 +568,7 @@ impl Request { /// let Body { apples } = surf::get(url).recv_form().await?; /// # Ok(()) } /// ``` - pub async fn recv_form(self) -> Result { + pub async fn recv_form(self) -> Result { let mut req = self.await?; Ok(req.body_form::().await?) } @@ -561,7 +580,7 @@ impl Request { } impl Future for Request { - type Output = Result; + type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if self.fut.is_none() { @@ -573,7 +592,7 @@ impl Future for Request { self.fut = Some(Box::pin(async move { let next = Next::new(&middleware, &|req, client| { - Box::pin(async move { client.send(req).await.map_err(|e| e.into()) }) + Box::pin(async move { client.send(req).await.map_err(Error::new) }) }); let res = next.run(req, client).await?; diff --git a/src/response.rs b/src/response.rs index 007beaf..6a39d09 100644 --- a/src/response.rs +++ b/src/response.rs @@ -11,7 +11,7 @@ use std::task::{Context, Poll}; use crate::headers::Headers; use crate::http_client; -use crate::Exception; +use crate::Error; /// An HTTP response, returned by `Request`. pub struct Response { @@ -29,8 +29,9 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let res = surf::get("https://httpbin.org/get").await?; /// assert_eq!(res.status(), 200); /// # Ok(()) } @@ -44,8 +45,9 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::http::version::Version; /// /// let res = surf::get("https://httpbin.org/get").await?; @@ -61,8 +63,9 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let res = surf::get("https://httpbin.org/get").await?; /// assert!(res.header("Content-Length").is_some()); /// # Ok(()) } @@ -77,8 +80,9 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), surf::Exception> { + /// # async fn main() -> Result<(), surf::Error> { /// let mut res = surf::post("https://httpbin.org/get").await?; /// for (name, value) in res.headers() { /// println!("{}: {}", name, value); @@ -102,8 +106,9 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// use surf::mime; /// let res = surf::get("https://httpbin.org/json").await?; /// assert_eq!(res.mime(), Some(mime::APPLICATION_JSON)); @@ -127,15 +132,16 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let mut res = surf::get("https://httpbin.org/get").await?; /// let bytes: Vec = res.body_bytes().await?; /// # Ok(()) } /// ``` - pub async fn body_bytes(&mut self) -> io::Result> { + pub async fn body_bytes(&mut self) -> Result, Error> { let mut buf = Vec::with_capacity(1024); - self.response.body_mut().read_to_end(&mut buf).await?; + self.response.body_mut().read_to_end(&mut buf).await.map_err(Error::new)?; Ok(buf) } @@ -163,14 +169,15 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// let mut res = surf::get("https://httpbin.org/get").await?; /// let string: String = res.body_string().await?; /// # Ok(()) } /// ``` - pub async fn body_string(&mut self) -> Result { - let bytes = self.body_bytes().await?; + pub async fn body_string(&mut self) -> Result { + let bytes = self.body_bytes().await.map_err(Error::new)?; let mime = self.mime(); let claimed_encoding = mime .as_ref() @@ -192,9 +199,10 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Deserialize, Serialize)] /// struct Ip { /// ip: String @@ -204,9 +212,9 @@ impl Response { /// let Ip { ip } = res.body_json().await?; /// # Ok(()) } /// ``` - pub async fn body_json(&mut self) -> std::io::Result { + pub async fn body_json(&mut self) -> Result { let body_bytes = self.body_bytes().await?; - Ok(serde_json::from_slice(&body_bytes).map_err(io::Error::from)?) + serde_json::from_slice(&body_bytes).map_err(Error::new) } /// Reads and deserialized the entire request body from form encoding. @@ -222,9 +230,10 @@ impl Response { /// # Examples /// /// ```no_run + /// # use surf::error::BoxError; /// # use serde::{Deserialize, Serialize}; /// # #[async_std::main] - /// # async fn main() -> Result<(), Box> { + /// # async fn main() -> Result<(), BoxError> { /// #[derive(Deserialize, Serialize)] /// struct Body { /// apples: u32 @@ -234,10 +243,9 @@ impl Response { /// let Body { apples } = res.body_form().await?; /// # Ok(()) } /// ``` - pub async fn body_form(&mut self) -> Result { + pub async fn body_form(&mut self) -> Result { let string = self.body_string().await?; - Ok(serde_urlencoded::from_str(&string) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?) + serde_urlencoded::from_str(&string).map_err(Error::new) } } @@ -312,7 +320,7 @@ fn is_utf8_encoding(encoding_label: &str) -> bool { /// If the body cannot be decoded as utf-8, this function returns an `std::io::Error` of kind /// `std::io::ErrorKind::InvalidData`, carrying a `DecodeError` struct. #[cfg(not(feature = "encoding"))] -fn decode_body(bytes: Vec, content_encoding: Option<&str>) -> Result { +fn decode_body(bytes: Vec, content_encoding: Option<&str>) -> Result { if is_utf8_encoding(content_encoding.unwrap_or("utf-8")) { Ok(String::from_utf8(bytes).map_err(|err| { let err = DecodeError { @@ -326,7 +334,7 @@ fn decode_body(bytes: Vec, content_encoding: Option<&str>) -> Result, content_encoding: Option<&str>) -> Result, content_encoding: Option<&str>) -> Result { +fn decode_body(bytes: Vec, content_encoding: Option<&str>) -> Result { use encoding_rs::Encoding; use std::borrow::Cow; @@ -352,7 +360,7 @@ fn decode_body(bytes: Vec, content_encoding: Option<&str>) -> Result, content_encoding: Option<&str>) -> Result, content_encoding: Option<&str>) -> Result, content_encoding: Option<&str>) -> Result { +fn decode_body(mut bytes: Vec, content_encoding: Option<&str>) -> Result { use web_sys::TextDecoder; // Encoding names are always valid ASCII, so we can avoid including casing mapping tables diff --git a/tests/test.rs b/tests/test.rs index 10c0367..bdaeaf2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,7 @@ +use surf::error::BoxError; + #[async_std::test] -async fn post_json() -> Result<(), surf::Exception> { +async fn post_json() -> Result<(), BoxError> { #[derive(serde::Deserialize, serde::Serialize)] struct Cat { name: String, @@ -17,7 +19,7 @@ async fn post_json() -> Result<(), surf::Exception> { } #[async_std::test] -async fn get_json() -> Result<(), surf::Exception> { +async fn get_json() -> Result<(), BoxError> { #[derive(serde::Deserialize)] struct Ip { ip: String,