rskit_httpclient/
request.rs1use crate::auth::Auth;
4use http::Method;
5use std::collections::HashMap;
6
7#[derive(Debug, Clone)]
9pub struct Request {
10 pub method: Method,
12
13 pub path: String,
15
16 pub headers: HashMap<String, String>,
18
19 pub body: Option<RequestBody>,
21
22 pub query: Option<HashMap<String, String>>,
24
25 pub auth: Option<Auth>,
27}
28
29#[derive(Debug, Clone)]
31#[non_exhaustive]
32pub enum RequestBody {
33 Json(serde_json::Value),
35 Text(String),
37 Bytes(bytes::Bytes),
39}
40
41impl RequestBody {
42 pub fn json<T: serde::Serialize>(value: &T) -> Result<Self, serde_json::Error> {
44 Ok(RequestBody::Json(serde_json::to_value(value)?))
45 }
46
47 pub fn text(text: impl Into<String>) -> Self {
49 RequestBody::Text(text.into())
50 }
51
52 pub fn bytes(bytes: impl Into<bytes::Bytes>) -> Self {
54 RequestBody::Bytes(bytes.into())
55 }
56}
57
58impl Request {
59 pub fn get(path: impl Into<String>) -> Self {
61 Self::new(Method::GET, path)
62 }
63
64 pub fn post(path: impl Into<String>) -> Self {
66 Self::new(Method::POST, path)
67 }
68
69 pub fn put(path: impl Into<String>) -> Self {
71 Self::new(Method::PUT, path)
72 }
73
74 pub fn patch(path: impl Into<String>) -> Self {
76 Self::new(Method::PATCH, path)
77 }
78
79 pub fn delete(path: impl Into<String>) -> Self {
81 Self::new(Method::DELETE, path)
82 }
83
84 pub fn head(path: impl Into<String>) -> Self {
86 Self::new(Method::HEAD, path)
87 }
88
89 pub fn new(method: Method, path: impl Into<String>) -> Self {
91 Self {
92 method,
93 path: path.into(),
94 headers: HashMap::new(),
95 body: None,
96 query: None,
97 auth: None,
98 }
99 }
100
101 pub fn path(mut self, path: impl Into<String>) -> Self {
103 self.path = path.into();
104 self
105 }
106
107 pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
109 self.headers.insert(name.into(), value.into());
110 self
111 }
112
113 pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
115 self.headers = headers;
116 self
117 }
118
119 pub fn body(mut self, body: RequestBody) -> Self {
121 self.body = Some(body);
122 self
123 }
124
125 pub fn json_body<T: serde::Serialize>(mut self, value: &T) -> Result<Self, serde_json::Error> {
127 self.body = Some(RequestBody::json(value)?);
128 Ok(self)
129 }
130
131 pub fn query(mut self, params: HashMap<String, String>) -> Self {
133 self.query = Some(params);
134 self
135 }
136
137 pub fn query_param(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
139 if self.query.is_none() {
140 self.query = Some(HashMap::new());
141 }
142 if let Some(ref mut q) = self.query {
143 q.insert(name.into(), value.into());
144 }
145 self
146 }
147
148 pub fn auth(mut self, auth: Auth) -> Self {
150 self.auth = Some(auth);
151 self
152 }
153
154 pub fn bearer_token(self, token: impl Into<String>) -> Self {
156 self.auth(Auth::bearer(token))
157 }
158
159 pub fn basic_auth(self, username: impl Into<String>, password: impl Into<String>) -> Self {
161 self.auth(Auth::basic(username, password))
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_request_builder() {
171 let req = Request::get("/api/users")
172 .header("X-Custom", "value")
173 .query_param("limit", "10")
174 .bearer_token("secret-token");
175
176 assert_eq!(req.method, Method::GET);
177 assert_eq!(req.path, "/api/users");
178 assert_eq!(req.headers.get("X-Custom"), Some(&"value".to_string()));
179 assert_eq!(
180 req.query.as_ref().unwrap().get("limit"),
181 Some(&"10".to_string())
182 );
183 assert!(matches!(req.auth, Some(Auth::Bearer(_))));
184 }
185
186 #[test]
187 fn test_request_body_json() {
188 let body = RequestBody::json(&serde_json::json!({"key": "value"})).unwrap();
189 assert!(matches!(body, RequestBody::Json(_)));
190 }
191}