Provide .method() and .query_params()

This commit is contained in:
Martin Algesten
2021-01-26 20:59:55 +01:00
parent c4c1618042
commit 239ba342a2
3 changed files with 88 additions and 46 deletions

View File

@@ -1,4 +1,4 @@
use url::Url; use url::{ParseError, Url};
use std::error; use std::error;
use std::fmt::{self, Display}; use std::fmt::{self, Display};
@@ -315,6 +315,14 @@ impl From<Transport> for Error {
} }
} }
impl From<ParseError> for Error {
fn from(err: ParseError) -> Self {
ErrorKind::InvalidUrl
.msg(&format!("failed to parse URL: {:?}", err))
.src(err)
}
}
impl fmt::Display for ErrorKind { impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {

View File

@@ -4,7 +4,6 @@ use std::{fmt, time};
use url::{form_urlencoded, Url}; use url::{form_urlencoded, Url};
use crate::body::Payload; use crate::body::Payload;
use crate::error::ErrorKind;
use crate::header::{self, Header}; use crate::header::{self, Header};
use crate::unit::{self, Unit}; use crate::unit::{self, Unit};
use crate::Response; use crate::Response;
@@ -15,10 +14,29 @@ use super::SerdeValue;
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone)] #[derive(Clone)]
enum Urlish { struct ParsedUrl(std::result::Result<Url, url::ParseError>);
Url(Url),
Str(String), impl fmt::Display for ParsedUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(url) = &self.0 {
write!(f, "{}", url.as_str())
} else {
write!(f, "{:?}", self.0)
}
}
}
impl From<String> for ParsedUrl {
fn from(s: String) -> Self {
ParsedUrl(s.parse())
}
}
impl From<Url> for ParsedUrl {
fn from(url: Url) -> Self {
ParsedUrl(Ok(url))
}
} }
/// Request instances are builders that creates a request. /// Request instances are builders that creates a request.
@@ -36,53 +54,38 @@ enum Urlish {
pub struct Request { pub struct Request {
agent: Agent, agent: Agent,
method: String, method: String,
url: Urlish, parsed_url: ParsedUrl,
error_on_non_2xx: bool, error_on_non_2xx: bool,
headers: Vec<Header>, headers: Vec<Header>,
query_params: Vec<(String, String)>,
timeout: Option<time::Duration>, timeout: Option<time::Duration>,
} }
impl fmt::Display for Urlish {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Urlish::Url(u) => write!(f, "{}", u),
Urlish::Str(s) => write!(f, "{}", s),
}
}
}
impl fmt::Debug for Request { impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"Request({} {} {:?}, {:?})", "Request({} {}, {:?})",
self.method, self.url, self.query_params, self.headers self.method, self.parsed_url, self.headers
) )
} }
} }
impl Request { impl Request {
pub(crate) fn new(agent: Agent, method: String, url: String) -> Request { pub(crate) fn new(agent: Agent, method: String, url: String) -> Request {
Request { Self::_new(agent, method, url.into())
agent,
method,
url: Urlish::Str(url),
headers: vec![],
error_on_non_2xx: true,
query_params: vec![],
timeout: None,
}
} }
pub(crate) fn with_url(agent: Agent, method: String, url: Url) -> Request { pub(crate) fn with_url(agent: Agent, method: String, url: Url) -> Request {
Self::_new(agent, method, url.into())
}
fn _new(agent: Agent, method: String, parsed_url: ParsedUrl) -> Request {
Request { Request {
agent, agent,
method, method,
url: Urlish::Url(url), parsed_url,
headers: vec![], headers: vec![],
error_on_non_2xx: true, error_on_non_2xx: true,
query_params: vec![],
timeout: None, timeout: None,
} }
} }
@@ -115,17 +118,8 @@ impl Request {
for h in &self.headers { for h in &self.headers {
h.validate()?; h.validate()?;
} }
let mut url: Url = match self.url { let url = self.parsed_url.0?;
Urlish::Url(u) => u,
Urlish::Str(s) => s.parse().map_err(|e| {
ErrorKind::InvalidUrl
.msg(&format!("failed to parse URL: {:?}", e))
.src(e)
})?,
};
for (name, value) in self.query_params {
url.query_pairs_mut().append_pair(&name, &value);
}
let deadline = match self.timeout.or(self.agent.config.timeout) { let deadline = match self.timeout.or(self.agent.config.timeout) {
None => None, None => None,
Some(timeout) => { Some(timeout) => {
@@ -349,7 +343,7 @@ impl Request {
/// ``` /// ```
/// # fn main() -> Result<(), ureq::Error> { /// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true); /// # ureq::is_test(true);
/// let resp = ureq::get("http://httpbin.org/response-headers") /// let resp = ureq::get("http://httpbin.org/get")
/// .query("format", "json") /// .query("format", "json")
/// .query("dest", "/login") /// .query("dest", "/login")
/// .call()?; /// .call()?;
@@ -357,10 +351,50 @@ impl Request {
/// # } /// # }
/// ``` /// ```
pub fn query(mut self, param: &str, value: &str) -> Self { pub fn query(mut self, param: &str, value: &str) -> Self {
self.query_params if let Ok(url) = &mut self.parsed_url.0 {
.push((param.to_string(), value.to_string())); url.query_pairs_mut().append_pair(param, value);
}
self self
} }
/// Returns the value of the request method. Something like `GET`, `POST`, `PUT` etc.
///
/// ```
/// let req = ureq::put("http://httpbin.org/put");
///
/// assert_eq!(req.method(), "PUT");
/// ```
pub fn method(&self) -> &str {
&self.method
}
/// Returns all query parameters as a vector of key-value pairs.
///
/// ```
/// # fn main() -> Result<(), ureq::Error> {
/// # ureq::is_test(true);
/// let req = ureq::get("http://httpbin.org/get")
/// .query("foo", "42")
/// .query("foo", "43");
///
/// assert_eq!(req.query_params(), vec![
/// ("foo".to_string(), "42".to_string()),
/// ("foo".to_string(), "43".to_string())
/// ]);
/// # Ok(())
/// # }
/// ```
pub fn query_params(&self) -> Vec<(String, String)> {
let mut ret = vec![];
if let Ok(url) = &self.parsed_url.0 {
for (k, v) in url.query_pairs() {
ret.push((k.into(), v.into()));
}
}
ret
}
} }
#[test] #[test]

View File

@@ -136,7 +136,7 @@ fn request_debug() {
assert_eq!( assert_eq!(
s, s,
"Request(GET http://localhost/my/page [], [Authorization: abcdef, \ "Request(GET http://localhost/my/page, [Authorization: abcdef, \
Content-Length: 1234, Content-Type: application/json])" Content-Length: 1234, Content-Type: application/json])"
); );
@@ -148,7 +148,7 @@ fn request_debug() {
assert_eq!( assert_eq!(
s, s,
"Request(GET http://localhost/my/page?q=z [(\"foo\", \"bar baz\")], [Authorization: abcdef])" "Request(GET http://localhost/my/page?q=z&foo=bar+baz, [Authorization: abcdef])"
); );
} }