diff options
-rw-r--r-- | src/lib.rs | 82 |
1 files changed, 63 insertions, 19 deletions
diff --git a/src/lib.rs b/src/lib.rs index 82e7322f38c8..080e7d9c6670 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,14 +2,41 @@ //! //! This library provides a simplified API over the [cURL Rust //! bindings][] that resemble that of higher-level libraries such as -//! [reqwest][]. +//! [reqwest][]. All calls are synchronous. //! //! `crimp` is intended to be used in situations where HTTP client //! functionality is desired without adding a significant number of //! dependencies or sacrificing too much usability. //! +//! Using `crimp` to make HTTP requests is done using a simple +//! builder-pattern style API: +//! +//! ```rust +//! use crimp::{Method, Request}; +//! +//! let response = Request::new(Method::Get, "http://httpbin.org/get").unwrap() +//! .user_agent("crimp test suite").unwrap() +//! .send().unwrap(); +//! +//! assert_eq!(response.status, 200); +//! ``` +//! +//! The `json` feature (enabled by default) adds support for +//! automatically serialising and deserialising request/response +//! bodies using `serde_json`. +//! +//! If a feature from the underlying cURL library is missing, the +//! `Request::raw` method can be used as an escape hatch to deal with +//! the handle directly. Should you find yourself doing this, please +//! [file an issue][]. +//! +//! `crimp` does not currently expose functionality for re-using a +//! cURL Easy handle, meaning that keep-alive of HTTP connections and +//! the like is not supported. +//! //! [cURL Rust bindings]: https://docs.rs/curl //! [reqwest]: https://docs.rs/reqwest +//! [file an issue]: https://github.com/tazjin/crimp/issues extern crate curl; @@ -17,22 +44,26 @@ extern crate curl; #[cfg(feature = "json")] extern crate serde_json; use curl::easy::{Easy, List, ReadError}; -#[cfg(feature = "json")] use serde::Serialize; -#[cfg(feature = "json")] use serde::de::DeserializeOwned; use std::collections::HashMap; use std::io::Write; use std::string::{FromUtf8Error, ToString}; +#[cfg(feature = "json")] use serde::Serialize; +#[cfg(feature = "json")] use serde::de::DeserializeOwned; + #[cfg(test)] mod tests; -type CurlResult<T> = Result<T, curl::Error>; - /// HTTP method to use for the request. pub enum Method { Get, Post, Put, Patch, Delete } +/// Builder structure for an HTTP request. +/// +/// This is the primary API-type in `crimp`. After creating a new +/// request its parameters are modified using the various builder +/// methods until it is consumed by `send()`. pub struct Request<'a> { handle: Easy, headers: List, @@ -49,16 +80,28 @@ enum Body<'a> { } } +/// HTTP responses structure containing response data and headers. +/// +/// By default the `send()` function of the `Request` structure will +/// return a `Response<Vec<u8>>`. Convenience helpers exist for +/// decoding a string via `Response::as_string` or to a +/// `serde`-compatible type with `Response::as_json` (if the +/// `json`-feature is enabled). #[derive(Debug)] -pub struct CurlResponse<T> { +pub struct Response<T> { + /// HTTP status code of the response. pub status: u32, + + /// HTTP headers returned from the remote. pub headers: HashMap<String, String>, + + /// Body data from the HTTP response. pub body: T, } impl <'a> Request<'a> { /// Initiate an HTTP request with the given method and URL. - pub fn new(method: Method, url: &str) -> CurlResult<Self> { + pub fn new(method: Method, url: &str) -> Result<Self, curl::Error> { let mut handle = Easy::new(); handle.url(url)?; @@ -77,20 +120,21 @@ impl <'a> Request<'a> { }) } - /// Add a header to a request. - pub fn header(mut self, k: &str, v: &str) -> CurlResult<Self> { + /// Add an HTTP header to a request. + pub fn header(mut self, k: &str, v: &str) -> Result<Self, curl::Error> { self.headers.append(&format!("{}: {}", k, v))?; Ok(self) } - /// Set the User-Agent for this request. - pub fn user_agent<'b: 'a>(mut self, agent: &str) -> CurlResult<Self> { + /// Set the `User-Agent` for this request. By default this will be + /// set to cURL's standard user agent. + pub fn user_agent<'b: 'a>(mut self, agent: &str) -> Result<Self, curl::Error> { self.handle.useragent(agent)?; Ok(self) } /// Add a byte-array body to a request using the specified - /// Content-Type. + /// `Content-Type`. pub fn body(mut self, content_type: &'a str, data: &'a [u8]) -> Self { self.body = Body::Bytes { data, content_type }; self @@ -106,7 +150,7 @@ impl <'a> Request<'a> { /// Send the HTTP request and return a response structure /// containing the raw body. - pub fn send(mut self) -> CurlResult<CurlResponse<Vec<u8>>> { + pub fn send(mut self) -> Result<Response<Vec<u8>>, curl::Error> { // Create structures in which to store the response data: let mut headers = HashMap::new(); let mut body = vec![]; @@ -191,7 +235,7 @@ impl <'a> Request<'a> { transfer.perform()?; } - Ok(CurlResponse { + Ok(Response { status: self.handle.response_code()?, headers, body @@ -199,13 +243,13 @@ impl <'a> Request<'a> { } } -impl CurlResponse<Vec<u8>> { +impl Response<Vec<u8>> { /// Attempt to parse the HTTP response body as a UTF-8 encoded /// string. - pub fn as_string(self) -> Result<CurlResponse<String>, FromUtf8Error> { + pub fn as_string(self) -> Result<Response<String>, FromUtf8Error> { let body = String::from_utf8(self.body)?; - Ok(CurlResponse { + Ok(Response { body, status: self.status, headers: self.headers, @@ -214,10 +258,10 @@ impl CurlResponse<Vec<u8>> { /// Attempt to deserialize the HTTP response body from JSON. #[cfg(feature = "json")] - pub fn as_json<T: DeserializeOwned>(self) -> Result<CurlResponse<T>, serde_json::Error> { + pub fn as_json<T: DeserializeOwned>(self) -> Result<Response<T>, serde_json::Error> { let deserialized = serde_json::from_slice(&self.body)?; - Ok(CurlResponse { + Ok(Response { body: deserialized, status: self.status, headers: self.headers, |