From 239ba342a23100ba98e03e40411ad6666a5a4225 Mon Sep 17 00:00:00 2001 From: Martin Algesten Date: Tue, 26 Jan 2021 20:59:55 +0100 Subject: [PATCH] Provide .method() and .query_params() --- src/error.rs | 10 +++- src/request.rs | 120 +++++++++++++++++++++++++++++---------------- src/test/simple.rs | 4 +- 3 files changed, 88 insertions(+), 46 deletions(-) diff --git a/src/error.rs b/src/error.rs index 102bf03..15a9aed 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use url::Url; +use url::{ParseError, Url}; use std::error; use std::fmt::{self, Display}; @@ -315,6 +315,14 @@ impl From for Error { } } +impl From for Error { + fn from(err: ParseError) -> Self { + ErrorKind::InvalidUrl + .msg(&format!("failed to parse URL: {:?}", err)) + .src(err) + } +} + impl fmt::Display for ErrorKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/src/request.rs b/src/request.rs index dc6caec..1505fcc 100644 --- a/src/request.rs +++ b/src/request.rs @@ -4,7 +4,6 @@ use std::{fmt, time}; use url::{form_urlencoded, Url}; use crate::body::Payload; -use crate::error::ErrorKind; use crate::header::{self, Header}; use crate::unit::{self, Unit}; use crate::Response; @@ -15,10 +14,29 @@ use super::SerdeValue; pub type Result = std::result::Result; -#[derive(Debug, Clone)] -enum Urlish { - Url(Url), - Str(String), +#[derive(Clone)] +struct ParsedUrl(std::result::Result); + +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 for ParsedUrl { + fn from(s: String) -> Self { + ParsedUrl(s.parse()) + } +} + +impl From for ParsedUrl { + fn from(url: Url) -> Self { + ParsedUrl(Ok(url)) + } } /// Request instances are builders that creates a request. @@ -36,53 +54,38 @@ enum Urlish { pub struct Request { agent: Agent, method: String, - url: Urlish, + parsed_url: ParsedUrl, error_on_non_2xx: bool, headers: Vec
, - query_params: Vec<(String, String)>, timeout: Option, } -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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "Request({} {} {:?}, {:?})", - self.method, self.url, self.query_params, self.headers + "Request({} {}, {:?})", + self.method, self.parsed_url, self.headers ) } } impl Request { pub(crate) fn new(agent: Agent, method: String, url: String) -> Request { - Request { - agent, - method, - url: Urlish::Str(url), - headers: vec![], - error_on_non_2xx: true, - query_params: vec![], - timeout: None, - } + Self::_new(agent, method, url.into()) } 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 { agent, method, - url: Urlish::Url(url), + parsed_url, headers: vec![], error_on_non_2xx: true, - query_params: vec![], timeout: None, } } @@ -115,17 +118,8 @@ impl Request { for h in &self.headers { h.validate()?; } - let mut url: Url = match self.url { - 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 url = self.parsed_url.0?; + let deadline = match self.timeout.or(self.agent.config.timeout) { None => None, Some(timeout) => { @@ -349,7 +343,7 @@ impl Request { /// ``` /// # fn main() -> Result<(), ureq::Error> { /// # 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("dest", "/login") /// .call()?; @@ -357,10 +351,50 @@ impl Request { /// # } /// ``` pub fn query(mut self, param: &str, value: &str) -> Self { - self.query_params - .push((param.to_string(), value.to_string())); + if let Ok(url) = &mut self.parsed_url.0 { + url.query_pairs_mut().append_pair(param, value); + } 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] diff --git a/src/test/simple.rs b/src/test/simple.rs index 842bc37..782f947 100644 --- a/src/test/simple.rs +++ b/src/test/simple.rs @@ -136,7 +136,7 @@ fn request_debug() { assert_eq!( s, - "Request(GET http://localhost/my/page [], [Authorization: abcdef, \ + "Request(GET http://localhost/my/page, [Authorization: abcdef, \ Content-Length: 1234, Content-Type: application/json])" ); @@ -148,7 +148,7 @@ fn request_debug() { assert_eq!( 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])" ); }