Skip to main content

rustbasic_core/support/
http_client.rs

1use std::time::Duration;
2use serde::Serialize;
3use serde::de::DeserializeOwned;
4use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
5
6pub struct Http;
7
8pub struct PendingRequest {
9    method: reqwest::Method,
10    url: String,
11    headers: HeaderMap,
12    query: Option<serde_json::Value>,
13    json_body: Option<serde_json::Value>,
14    timeout: Option<Duration>,
15}
16
17pub struct Response {
18    inner: reqwest::Response,
19}
20
21impl Http {
22    pub fn get(url: &str) -> PendingRequest {
23        PendingRequest::new(reqwest::Method::GET, url)
24    }
25
26    pub fn post(url: &str) -> PendingRequest {
27        PendingRequest::new(reqwest::Method::POST, url)
28    }
29
30    pub fn put(url: &str) -> PendingRequest {
31        PendingRequest::new(reqwest::Method::PUT, url)
32    }
33
34    pub fn patch(url: &str) -> PendingRequest {
35        PendingRequest::new(reqwest::Method::PATCH, url)
36    }
37
38    pub fn delete(url: &str) -> PendingRequest {
39        PendingRequest::new(reqwest::Method::DELETE, url)
40    }
41}
42
43impl PendingRequest {
44    pub fn new(method: reqwest::Method, url: &str) -> Self {
45        Self {
46            method,
47            url: url.to_string(),
48            headers: HeaderMap::new(),
49            query: None,
50            json_body: None,
51            timeout: None,
52        }
53    }
54
55    pub fn with_headers(mut self, headers: std::collections::HashMap<String, String>) -> Self {
56        for (k, v) in headers {
57            if let (Ok(hname), Ok(hval)) = (HeaderName::from_bytes(k.as_bytes()), HeaderValue::from_str(&v)) {
58                self.headers.insert(hname, hval);
59            }
60        }
61        self
62    }
63
64    pub fn header(mut self, key: &str, value: &str) -> Self {
65        if let (Ok(hname), Ok(hval)) = (HeaderName::from_bytes(key.as_bytes()), HeaderValue::from_str(value)) {
66            self.headers.insert(hname, hval);
67        }
68        self
69    }
70
71    pub fn with_token(self, token: &str) -> Self {
72        self.header("Authorization", &format!("Bearer {}", token))
73    }
74
75    pub fn basic_auth(self, username: &str, password: Option<&str>) -> Self {
76        let auth_str = format!("{}:{}", username, password.unwrap_or(""));
77        let encoded = crate::base64::encode(auth_str.as_bytes());
78        self.header("Authorization", &format!("Basic {}", encoded))
79    }
80
81    pub fn query(mut self, query: impl Serialize) -> Self {
82        self.query = Some(serde_json::to_value(query).unwrap_or(serde_json::Value::Null));
83        self
84    }
85
86    pub fn json(mut self, body: impl Serialize) -> Self {
87        self.json_body = Some(serde_json::to_value(body).unwrap_or(serde_json::Value::Null));
88        self
89    }
90
91    pub fn timeout(mut self, duration: Duration) -> Self {
92        self.timeout = Some(duration);
93        self
94    }
95
96    pub async fn send(self) -> Result<Response, reqwest::Error> {
97        let mut client_builder = reqwest::Client::builder();
98        if let Some(t) = self.timeout {
99            client_builder = client_builder.timeout(t);
100        }
101        let client = client_builder.build()?;
102
103        let mut req_builder = client.request(self.method, &self.url);
104        req_builder = req_builder.headers(self.headers);
105
106        if let Some(q) = self.query {
107            req_builder = req_builder.query(&q);
108        }
109
110        if let Some(b) = self.json_body {
111            req_builder = req_builder.json(&b);
112        }
113
114        let resp = req_builder.send().await?;
115        Ok(Response { inner: resp })
116    }
117}
118
119impl Response {
120    pub fn status(&self) -> reqwest::StatusCode {
121        self.inner.status()
122    }
123
124    pub fn is_success(&self) -> bool {
125        self.inner.status().is_success()
126    }
127
128    pub async fn text(self) -> Result<String, reqwest::Error> {
129        self.inner.text().await
130    }
131
132    pub async fn json<T: DeserializeOwned>(self) -> Result<T, reqwest::Error> {
133        self.inner.json::<T>().await
134    }
135
136    pub async fn json_value(self) -> Result<serde_json::Value, reqwest::Error> {
137        self.inner.json::<serde_json::Value>().await
138    }
139}