1use std::collections::HashMap;
2
3use crate::config::Config;
4use crate::oauth1;
5use crate::params::Params;
6use crate::response::Response;
7use crate::Error;
8use http::Method;
9use log::debug;
10
11#[derive(Debug, Clone)]
12pub struct Requester {
13 config: Config,
14 base_url: String,
15 algorithm: oauth1::Algorithm,
16}
17
18impl Requester {
19 pub fn new(config: Config, base_url: String) -> Self {
20 Self {
21 config,
22 base_url,
23 algorithm: oauth1::Algorithm::Sha256,
24 }
25 }
26
27 pub fn with_base_url(self, base_url: String) -> Self {
28 Self { base_url, ..self }
29 }
30
31 pub fn with_algorithm(self, algorithm: oauth1::Algorithm) -> Self {
32 Self { algorithm, ..self }
33 }
34
35 fn make_url(&self, endpoint: &str) -> String {
36 format!("{}/{}", self.base_url, endpoint.trim_start_matches('/'))
37 }
38
39 fn auth_header(&self, method: &Method, url: &str, params: &Option<Params>) -> String {
40 oauth1::authorize(
41 method.as_str(),
42 url,
43 &self.config.consumer,
44 Some(&self.config.token),
45 params.clone().map(|p| p.into()),
46 Some(&self.config.account),
47 self.algorithm,
48 )
49 }
50
51 pub fn request(
52 &self,
53 method: Method,
54 endpoint: &str,
55 params: Option<Params>,
56 headers: Option<Params>,
57 payload: Option<&str>,
58 ) -> Result<Response, Error> {
59 let url = self.make_url(endpoint);
60 let auth = self.auth_header(&method, &url, ¶ms);
61
62 let mut req = ureq::request(method.as_str(), &url)
63 .set("Authorization", &auth)
64 .set("Content-Type", "application/json");
65
66 if let Some(params) = params {
67 for (k, v) in params.get() {
68 req = req.query(k, v);
69 }
70 }
71
72 if let Some(headers) = headers {
73 for (k, v) in headers.get() {
74 req = req.set(k, v);
75 }
76 }
77
78 let resp = if let Some(payload) = payload {
79 debug!("Payload: {}", payload);
80 req.send_string(payload)
81 } else {
82 req.send_string("")
83 };
84
85 let resp = match resp {
86 Ok(resp) => resp,
87 Err(ureq::Error::Status(code, resp)) => {
88 return Err(Error::HttpRequestError(
89 code,
90 resp.into_string().unwrap_or_default(),
91 ));
92 }
93 Err(ureq::Error::Transport(transport)) => {
94 return Err(Error::HttpTransportError(transport.to_string()));
95 }
96 };
97 let status = http::StatusCode::from_u16(resp.status()).unwrap();
98 let headers: HashMap<String, String> = resp
99 .headers_names()
100 .into_iter()
101 .filter_map(|name| resp.header(&name).map(|value| (name, value.to_owned())))
102 .collect();
103
104 let parsed = Response::new(status, headers, resp.into_string()?);
105 debug!("Response: {}", &parsed);
106 Ok(parsed)
107 }
108}