diff --git a/src/agent.rs b/src/agent.rs index c50aa2f..3efeacb 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -10,6 +10,34 @@ include!("response.rs"); include!("conn.rs"); include!("stream.rs"); +/// Agents keep state between requests. +/// +/// By default, no state, such as cookies, is kept between requests. +/// But by creating an agent as entry point for the request, we +/// can keep a state. +/// +/// ``` +/// let agent = ureq::agent().build(); +/// +/// let auth = agent +/// .post("/login") +/// .auth("martin", "rubbermashgum") +/// .call(); // blocks. puts auth cookies in agent. +/// +/// if !auth.ok() { +/// println!("Noes!"); +/// } +/// +/// let secret = agent +/// .get("/my-protected-page") +/// .call(); // blocks and waits for request. +/// +/// if !secret.ok() { +/// println!("Wot?!"); +/// } +/// +/// println!("Secret is: {}", secret.into_string().unwrap()); +/// ``` #[derive(Debug, Default, Clone)] pub struct Agent { headers: Vec
, @@ -32,6 +60,16 @@ impl AgentState { } impl Agent { + /// Creates a new agent. Typically you'd use [`ureq::agent()`](fn.agent.html) to + /// do this. + /// + /// ``` + /// let agent = ureq::Agent::new() + /// .set("X-My-Header", "Foo") // present on all requests from this agent + /// .build(); + /// + /// agent.get("/foo"); + /// ``` pub fn new() -> Agent { Default::default() } @@ -83,7 +121,7 @@ impl Agent { /// /// fn main() { /// let agent = ureq::agent() - /// .set_map(map!{ + /// .set_map(map! { /// "X-API-Key" => "foobar", /// "Accept" => "application/json" /// }) @@ -213,54 +251,71 @@ impl Agent { } } + /// Make a GET request from this agent. pub fn get(&self, path: S) -> Request where S: Into, { self.request("GET", path) } + + /// Make a HEAD request from this agent. pub fn head(&self, path: S) -> Request where S: Into, { self.request("HEAD", path) } + + /// Make a POST request from this agent. pub fn post(&self, path: S) -> Request where S: Into, { self.request("POST", path) } + + /// Make a PUT request from this agent. pub fn put(&self, path: S) -> Request where S: Into, { self.request("PUT", path) } + + /// Make a DELETE request from this agent. pub fn delete(&self, path: S) -> Request where S: Into, { self.request("DELETE", path) } + + /// Make a TRACE request from this agent. pub fn trace(&self, path: S) -> Request where S: Into, { self.request("TRACE", path) } + + /// Make a OPTIONS request from this agent. pub fn options(&self, path: S) -> Request where S: Into, { self.request("OPTIONS", path) } + + /// Make a CONNECT request from this agent. pub fn connect(&self, path: S) -> Request where S: Into, { self.request("CONNECT", path) } + + /// Make a PATCH request from this agent. pub fn patch(&self, path: S) -> Request where S: Into, diff --git a/src/header.rs b/src/header.rs index 662d9f6..0d6b3cc 100644 --- a/src/header.rs +++ b/src/header.rs @@ -13,7 +13,9 @@ impl Header { /// The header name. /// /// ``` - /// let header = "X-Forwarded-For: 127.0.0.1".parse::().unwrap(); + /// let header = "X-Forwarded-For: 127.0.0.1" + /// .parse::() + /// .unwrap(); /// assert_eq!("X-Forwarded-For", header.name()); /// ``` pub fn name(&self) -> &str { @@ -23,7 +25,9 @@ impl Header { /// The header value. /// /// ``` - /// let header = "X-Forwarded-For: 127.0.0.1".parse::().unwrap(); + /// let header = "X-Forwarded-For: 127.0.0.1" + /// .parse::() + /// .unwrap(); /// assert_eq!("127.0.0.1", header.value()); /// ``` pub fn value(&self) -> &str { @@ -33,7 +37,9 @@ impl Header { /// Compares the given str to the header name ignoring case. /// /// ``` - /// let header = "X-Forwarded-For: 127.0.0.1".parse::().unwrap(); + /// let header = "X-Forwarded-For: 127.0.0.1" + /// .parse::() + /// .unwrap(); /// assert!(header.is_name("x-forwarded-for")); /// ``` pub fn is_name(&self, other: &str) -> bool { diff --git a/src/lib.rs b/src/lib.rs index dd7e574..a785a6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,28 @@ +//! ureq is a minimal request library. +//! +//! The goals of this library are: +//! +//! * Minimal dependency tree +//! * Obvious API +//! +//! # Plain requests +//! +//! Most standard methods (GET, POST, PUT etc), are supported as functions from the +//! top of the library ([`ureq::get`](fn.get.html), [`ureq::post`](fn.post.html), +//! [`ureq::put`](fn.out.html), etc). +//! +//! These top level http method functions create a [Request](struct.Request.html) instance +//! which follows a build pattern. The builders are finished using +//! [`.call()`](struct.Request.html#method.call), +//! [`.send_str()`](struct.Request.html#method.send_str) or +//! [`.send_json()`](struct.Request.html#method.send_json). +//! +//! # Agents +//! +//! To maintain a state, cookies, between requests, you use an [agent](struct.Agent.html). +//! Agents also follow the build pattern. Agents are created with `ureq::agent().build()`. +//! + extern crate ascii; extern crate base64; extern crate chunked_transfer; @@ -25,41 +50,19 @@ pub use agent::{Agent, Request, Response}; pub use header::Header; // re-export -pub use serde_json::{to_value, Map, Value}; +pub use serde_json::{to_value as serde_to_value, Map as SerdeMap, Value as SerdeValue}; pub use cookie::Cookie; -/// Agents keep state between requests. -/// -/// By default, no state, such as cookies, is kept between requests. -/// But by creating an agent as entry point for the request, we -/// can keep state. -/// -/// ``` -/// let agent = ureq::agent(); -/// -/// let auth = agent -/// .post("/login") -/// .auth("martin", "rubbermashgum") -/// .call(); // blocks. puts auth cookies in agent. -/// -/// if !auth.ok() { -/// println!("Noes!"); -/// } -/// -/// let secret = agent -/// .get("/my-protected-page") -/// .call(); // blocks and waits for request. -/// -/// if !secret.ok() { -/// println!("Wot?!"); -/// } -/// -/// println!("Secret is: {}", secret.into_string().unwrap()); -/// ``` +/// Agents are used to keep state between requests. pub fn agent() -> Agent { Agent::new() } +/// Make a request setting the HTTP method via a string. +/// +/// ``` +/// ureq::request("GET", "https://www.google.com").call(); +/// ``` pub fn request(method: M, path: S) -> Request where M: Into, @@ -68,54 +71,71 @@ where Agent::new().request(method, path) } +/// Make a GET request. pub fn get(path: S) -> Request where S: Into, { request("GET", path) } + +/// Make a HEAD request. pub fn head(path: S) -> Request where S: Into, { request("HEAD", path) } + +/// Make a POST request. pub fn post(path: S) -> Request where S: Into, { request("POST", path) } + +/// Make a PUT request. pub fn put(path: S) -> Request where S: Into, { request("PUT", path) } + +/// Make a DELETE request. pub fn delete(path: S) -> Request where S: Into, { request("DELETE", path) } + +/// Make a TRACE request. pub fn trace(path: S) -> Request where S: Into, { request("TRACE", path) } + +/// Make an OPTIONS request. pub fn options(path: S) -> Request where S: Into, { request("OPTIONS", path) } + +/// Make an CONNECT request. pub fn connect(path: S) -> Request where S: Into, { request("CONNECT", path) } + +/// Make an PATCH request. pub fn patch(path: S) -> Request where S: Into, diff --git a/src/macros.rs b/src/macros.rs index cd33913..eda6fa7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,19 @@ + +/// Create a `HashMap` from a shorthand notation. +/// +/// ``` +/// #[macro_use] +/// extern crate ureq; +/// +/// fn main() { +/// let headers = map! { +/// "X-API-Key" => "foobar", +/// "Accept" => "application/json" +/// }; +/// +/// let agent = ureq::agent().set_map(headers).build(); +/// } +/// ``` #[macro_export] macro_rules! map( { $($key:expr => $value:expr),* } => { diff --git a/src/request.rs b/src/request.rs index f825cf1..c1024c1 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,13 +1,23 @@ use qstring::QString; -use serde_json; +use super::SerdeValue; use std::sync::Arc; use std::io::Cursor; use std::io::empty; +use serde_json; lazy_static! { static ref URL_BASE: Url = { Url::parse("http://localhost/").expect("Failed to parse URL_BASE") }; } +/// Request instances are builders that creates a request. +/// +/// ``` +/// let mut request = ureq::get("https://www.google.com/"); +/// +/// let response = request +/// .query("foo", "bar baz") // add ?foo=bar%20baz +/// .call(); // run the request +/// ``` #[derive(Clone, Default)] pub struct Request { state: Arc>>, @@ -28,7 +38,7 @@ pub struct Request { enum Payload { Empty, Text(String), - JSON(serde_json::Value), + JSON(SerdeValue), Reader(Box), } @@ -137,7 +147,7 @@ impl Request { /// println!("{:?}", r); /// } /// ``` - pub fn send_json(&mut self, data: serde_json::Value) -> Response { + pub fn send_json(&mut self, data: SerdeValue) -> Response { self.do_call(Payload::JSON(data)) } @@ -248,7 +258,7 @@ impl Request { /// /// fn main() { /// let r = ureq::get("/my_page") - /// .set_map(map!{ + /// .set_map(map! { /// "X-API-Key" => "foobar", /// "Accept" => "application/json" /// }) @@ -304,7 +314,7 @@ impl Request { /// /// fn main() { /// let r = ureq::get("/my_page") - /// .query_map(map!{ + /// .query_map(map! { /// "format" => "json", /// "dest" => "/login" /// }) diff --git a/src/response.rs b/src/response.rs index f0a9452..badb4b3 100644 --- a/src/response.rs +++ b/src/response.rs @@ -12,7 +12,11 @@ use error::Error; const DEFAULT_CONTENT_TYPE: &'static str = "text/plain"; const DEFAULT_CHARACTER_SET: &'static str = "utf-8"; +/// Response instances are created as results of firing off requests. +/// +/// pub struct Response { + error: Option, status_line: AsciiString, index: (usize, usize), // index into status_line where we split: HTTP/1.1 200 OK status: u16, @@ -32,22 +36,22 @@ impl ::std::fmt::Debug for Response { } impl Response { - /// The entire status line like: HTTP/1.1 200 OK + /// The entire status line like: `HTTP/1.1 200 OK` pub fn status_line(&self) -> &str { self.status_line.as_str() } - /// The http version: HTTP/1.1 + /// The http version: `HTTP/1.1` pub fn http_version(&self) -> &str { &self.status_line.as_str()[0..self.index.0] } - /// The status as a u16: 200 + /// The status as a u16: `200` pub fn status(&self) -> &u16 { &self.status } - /// The status text: OK + /// The status text: `OK` pub fn status_text(&self) -> &str { &self.status_line.as_str()[self.index.1 + 1..].trim() } @@ -170,15 +174,14 @@ impl Response { let is_chunked = self.header("transfer-encoding") .map(|enc| enc.len() > 0) // whatever it says, do chunked .unwrap_or(false); - let len = self.header("content-length").and_then(|l| l.parse::().ok()); + let len = self.header("content-length") + .and_then(|l| l.parse::().ok()); let reader = self.stream.expect("No reader in response?!"); match is_chunked { true => Box::new(chunked_transfer::Decoder::new(reader)), - false => { - match len { - Some(len) => Box::new(LimitedRead::new(reader, len)), - None => Box::new(reader) as Box, - } + false => match len { + Some(len) => Box::new(LimitedRead::new(reader, len)), + None => Box::new(reader) as Box, }, } } @@ -264,13 +267,11 @@ impl Response { /// /// assert_eq!(*resp.status(), 401); /// ``` - pub fn from_read(reader: impl Read) -> Self - { + pub fn from_read(reader: impl Read) -> Self { Self::do_from_read(reader).unwrap_or_else(|e| e.into()) } - fn do_from_read(mut reader: impl Read) -> Result - { + fn do_from_read(mut reader: impl Read) -> Result { // // HTTP/1.1 200 OK\r\n let status_line = read_next_line(&mut reader).map_err(|_| Error::BadStatus)?; @@ -305,7 +306,6 @@ impl Response { pub fn to_write_vec(&self) -> Vec { self.stream.as_ref().unwrap().to_write_vec() } - } fn parse_status_line(line: &str) -> Result<((usize, usize), u16), Error> { @@ -405,8 +405,8 @@ impl Read for LimitedRead { Ok(amount) => { self.position += amount; Ok(amount) - }, - Err(e) => Err(e) + } + Err(e) => Err(e), } } } diff --git a/src/serde_macros.rs b/src/serde_macros.rs index dcab2ac..1e6b7a6 100644 --- a/src/serde_macros.rs +++ b/src/serde_macros.rs @@ -247,32 +247,32 @@ macro_rules! json_internal { ////////////////////////////////////////////////////////////////////////// (null) => { - $crate::Value::Null + $crate::SerdeValue::Null }; (true) => { - $crate::Value::Bool(true) + $crate::SerdeValue::Bool(true) }; (false) => { - $crate::Value::Bool(false) + $crate::SerdeValue::Bool(false) }; ([]) => { - $crate::Value::Array(vec![]) + $crate::SerdeValue::Array(vec![]) }; ([ $($tt:tt)+ ]) => { - $crate::Value::Array(json_internal!(@array [] $($tt)+)) + $crate::SerdeValue::Array(json_internal!(@array [] $($tt)+)) }; ({}) => { - $crate::Value::Object($crate::Map::new()) + $crate::SerdeValue::Object($crate::SerdeMap::new()) }; ({ $($tt:tt)+ }) => { - $crate::Value::Object({ - let mut object = $crate::Map::new(); + $crate::SerdeValue::Object({ + let mut object = $crate::SerdeMap::new(); json_internal!(@object object () ($($tt)+) ($($tt)+)); object }) @@ -281,6 +281,6 @@ macro_rules! json_internal { // Any Serialize type: numbers, strings, struct literals, variables etc. // Must be below every other rule. ($other:expr) => { - $crate::to_value(&$other).unwrap() + $crate::serde_to_value(&$other).unwrap() }; }