1use crate::{CloudError as RestError, Result};
10use reqwest::Client;
11use serde::Serialize;
12use std::sync::Arc;
13
14#[derive(Debug, Clone)]
40pub struct CloudClientBuilder {
41 api_key: Option<String>,
42 api_secret: Option<String>,
43 base_url: String,
44 timeout: std::time::Duration,
45}
46
47impl Default for CloudClientBuilder {
48 fn default() -> Self {
49 Self {
50 api_key: None,
51 api_secret: None,
52 base_url: "https://api.redislabs.com/v1".to_string(),
53 timeout: std::time::Duration::from_secs(30),
54 }
55 }
56}
57
58impl CloudClientBuilder {
59 pub fn new() -> Self {
61 Self::default()
62 }
63
64 pub fn api_key(mut self, key: impl Into<String>) -> Self {
66 self.api_key = Some(key.into());
67 self
68 }
69
70 pub fn api_secret(mut self, secret: impl Into<String>) -> Self {
72 self.api_secret = Some(secret.into());
73 self
74 }
75
76 pub fn base_url(mut self, url: impl Into<String>) -> Self {
78 self.base_url = url.into();
79 self
80 }
81
82 pub fn timeout(mut self, timeout: std::time::Duration) -> Self {
84 self.timeout = timeout;
85 self
86 }
87
88 pub fn build(self) -> Result<CloudClient> {
90 let api_key = self
91 .api_key
92 .ok_or_else(|| RestError::ConnectionError("API key is required".to_string()))?;
93 let api_secret = self
94 .api_secret
95 .ok_or_else(|| RestError::ConnectionError("API secret is required".to_string()))?;
96
97 let client = Client::builder()
98 .timeout(self.timeout)
99 .build()
100 .map_err(|e| RestError::ConnectionError(e.to_string()))?;
101
102 Ok(CloudClient {
103 api_key,
104 api_secret,
105 base_url: self.base_url,
106 timeout: self.timeout,
107 client: Arc::new(client),
108 })
109 }
110}
111
112#[derive(Clone)]
114pub struct CloudClient {
115 pub(crate) api_key: String,
116 pub(crate) api_secret: String,
117 pub(crate) base_url: String,
118 #[allow(dead_code)]
119 pub(crate) timeout: std::time::Duration,
120 pub(crate) client: Arc<Client>,
121}
122
123impl CloudClient {
124 pub fn builder() -> CloudClientBuilder {
126 CloudClientBuilder::new()
127 }
128
129 pub async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T> {
131 let url = format!("{}{}", self.base_url, path);
132
133 let response = self
135 .client
136 .get(&url)
137 .header("x-api-key", &self.api_key)
138 .header("x-api-secret-key", &self.api_secret)
139 .send()
140 .await?;
141
142 self.handle_response(response).await
143 }
144
145 pub async fn post<B: Serialize, T: serde::de::DeserializeOwned>(
147 &self,
148 path: &str,
149 body: &B,
150 ) -> Result<T> {
151 let url = format!("{}{}", self.base_url, path);
152
153 let response = self
155 .client
156 .post(&url)
157 .header("x-api-key", &self.api_key)
158 .header("x-api-secret-key", &self.api_secret)
159 .json(body)
160 .send()
161 .await?;
162
163 self.handle_response(response).await
164 }
165
166 pub async fn put<B: Serialize, T: serde::de::DeserializeOwned>(
168 &self,
169 path: &str,
170 body: &B,
171 ) -> Result<T> {
172 let url = format!("{}{}", self.base_url, path);
173
174 let response = self
176 .client
177 .put(&url)
178 .header("x-api-key", &self.api_key)
179 .header("x-api-secret-key", &self.api_secret)
180 .json(body)
181 .send()
182 .await?;
183
184 self.handle_response(response).await
185 }
186
187 pub async fn delete(&self, path: &str) -> Result<()> {
189 let url = format!("{}{}", self.base_url, path);
190
191 let response = self
193 .client
194 .delete(&url)
195 .header("x-api-key", &self.api_key)
196 .header("x-api-secret-key", &self.api_secret)
197 .send()
198 .await?;
199
200 if response.status().is_success() {
201 Ok(())
202 } else {
203 let status = response.status();
204 let text = response.text().await.unwrap_or_default();
205 Err(RestError::ApiError {
206 code: status.as_u16(),
207 message: text,
208 })
209 }
210 }
211
212 pub async fn get_raw(&self, path: &str) -> Result<serde_json::Value> {
214 self.get(path).await
215 }
216
217 pub async fn post_raw(&self, path: &str, body: serde_json::Value) -> Result<serde_json::Value> {
219 self.post(path, &body).await
220 }
221
222 pub async fn put_raw(&self, path: &str, body: serde_json::Value) -> Result<serde_json::Value> {
224 self.put(path, &body).await
225 }
226
227 pub async fn patch_raw(
229 &self,
230 path: &str,
231 body: serde_json::Value,
232 ) -> Result<serde_json::Value> {
233 let url = format!("{}{}", self.base_url, path);
234
235 let response = self
237 .client
238 .patch(&url)
239 .header("x-api-key", &self.api_key)
240 .header("x-api-secret-key", &self.api_secret)
241 .json(&body)
242 .send()
243 .await?;
244
245 self.handle_response(response).await
246 }
247
248 pub async fn delete_raw(&self, path: &str) -> Result<serde_json::Value> {
250 let url = format!("{}{}", self.base_url, path);
251
252 let response = self
254 .client
255 .delete(&url)
256 .header("x-api-key", &self.api_key)
257 .header("x-api-secret-key", &self.api_secret)
258 .send()
259 .await?;
260
261 if response.status().is_success() {
262 if response.content_length() == Some(0) {
263 Ok(serde_json::json!({"status": "deleted"}))
264 } else {
265 response.json().await.map_err(Into::into)
266 }
267 } else {
268 let status = response.status();
269 let text = response.text().await.unwrap_or_default();
270 Err(RestError::ApiError {
271 code: status.as_u16(),
272 message: text,
273 })
274 }
275 }
276
277 async fn handle_response<T: serde::de::DeserializeOwned>(
279 &self,
280 response: reqwest::Response,
281 ) -> Result<T> {
282 let status = response.status();
283
284 if status.is_success() {
285 let text = response.text().await.map_err(|e| {
287 RestError::ConnectionError(format!("Failed to read response: {}", e))
288 })?;
289
290 serde_json::from_str::<T>(&text).map_err(|e| {
292 RestError::JsonError(e)
294 })
295 } else if status == 401 {
296 let text = response
298 .text()
299 .await
300 .unwrap_or_else(|_| "No error message".to_string());
301 Err(RestError::ApiError {
302 code: 401,
303 message: format!("Authentication failed: {}", text),
304 })
305 } else {
306 let text = response.text().await.unwrap_or_default();
307 Err(RestError::ApiError {
308 code: status.as_u16(),
309 message: text,
310 })
311 }
312 }
313}