1use super::DriverError;
2use reqwest;
3use serde_json::Value;
4use std::time::Duration;
5
6pub struct HttpClient {
7 base_url: String,
8 database: Option<String>,
9 token: Option<String>,
10 client: reqwest::Client,
11}
12
13impl HttpClient {
14 pub fn new(base_url: &str) -> Self {
15 let client = reqwest::Client::builder()
16 .timeout(Duration::from_secs(30))
17 .pool_idle_timeout(Duration::from_secs(30))
18 .pool_max_idle_per_host(16)
19 .build()
20 .unwrap();
21
22 Self {
23 base_url: base_url.to_string().trim_end_matches('/').to_string(),
24 database: None,
25 token: None,
26 client,
27 }
28 }
29
30 pub fn with_database(mut self, database: &str) -> Self {
31 self.database = Some(database.to_string());
32 self
33 }
34
35 pub fn set_database(&mut self, database: &str) {
36 self.database = Some(database.to_string());
37 }
38
39 pub async fn login(&mut self, database: &str, username: &str, password: &str) -> Result<(), DriverError> {
40 let response = self
41 .client
42 .post(&format!("{}/auth/login", self.base_url))
43 .json(&serde_json::json!({
44 "database": database,
45 "username": username,
46 "password": password
47 }))
48 .send()
49 .await
50 .map_err(|e| DriverError::ConnectionError(format!("HTTP request failed: {}", e)))?;
51
52 if response.status() == 401 {
53 return Err(DriverError::AuthError("Invalid credentials".to_string()));
54 }
55
56 let data: Value = response
57 .json()
58 .await
59 .map_err(|e| DriverError::ProtocolError(format!("Failed to parse login response: {}", e)))?;
60
61 if let Some(token) = data.get("token").and_then(|t| t.as_str()) {
62 self.token = Some(token.to_string());
63 self.database = Some(database.to_string());
64 Ok(())
65 } else {
66 Err(DriverError::AuthError("No token in response".to_string()))
67 }
68 }
69
70 pub fn set_token(&mut self, token: &str) {
71 self.token = Some(token.to_string());
72 }
73
74 fn get_headers(&self) -> reqwest::header::HeaderMap {
75 let mut headers = reqwest::header::HeaderMap::new();
76 headers.insert(reqwest::header::CONTENT_TYPE, "application/json".parse().unwrap());
77 if let Some(token) = &self.token {
78 headers.insert(
79 reqwest::header::AUTHORIZATION,
80 format!("Bearer {}", token).parse().unwrap(),
81 );
82 }
83 headers
84 }
85
86 async fn request<T>(&self, method: &str, path: &str, body: Option<&Value>) -> Result<T, DriverError>
87 where
88 T: serde::de::DeserializeOwned,
89 {
90 let url = format!("{}{}", self.base_url, path);
91 let mut request = self.client.request(method.parse().unwrap(), &url);
92 request = request.headers(self.get_headers());
93
94 if let Some(b) = body {
95 request = request.json(b);
96 }
97
98 let response = request
99 .send()
100 .await
101 .map_err(|e| DriverError::ConnectionError(format!("HTTP request failed: {}", e)))?;
102
103 let status = response.status();
104
105 if !status.is_success() {
106 let error_text = response
107 .text()
108 .await
109 .unwrap_or_else(|_| "Unknown error".to_string());
110 return Err(DriverError::ServerError(format!("HTTP {} {}: {}", status, path, error_text)));
111 }
112
113 let text = response
114 .text()
115 .await
116 .map_err(|e| DriverError::ProtocolError(format!("Failed to read response: {}", e)))?;
117
118 if text.is_empty() {
119 return Err(DriverError::ServerError(format!("Empty response for HTTP {} {}", method, path)));
120 }
121
122 serde_json::from_str(&text)
123 .map_err(|e| DriverError::ProtocolError(format!("Failed to parse response: {} - Text: {}", e, text)))
124 }
125
126 pub async fn list_databases(&self) -> Result<Vec<String>, DriverError> {
127 let response: Value = self.request("GET", "/_api/databases", None).await?;
128 Ok(response.get("databases")
129 .and_then(|d| d.as_array())
130 .map(|arr| arr.iter().filter_map(|s| s.as_str().map(|s| s.to_string())).collect())
131 .unwrap_or_default())
132 }
133
134 pub async fn create_database(&self, name: &str) -> Result<(), DriverError> {
135 self.request::<Value>("POST", "/_api/databases", Some(&serde_json::json!({"name": name}))).await?;
136 Ok(())
137 }
138
139 pub async fn list_collections(&self, database: Option<&str>) -> Result<Vec<Value>, DriverError> {
140 let db = database.or(self.database.as_deref()).ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
141 let response: Value = self.request("GET", &format!("/_api/database/{}/collection", db), None).await?;
142 Ok(response.get("collections")
143 .and_then(|c| c.as_array())
144 .cloned()
145 .unwrap_or_default())
146 }
147
148 pub async fn create_collection(&self, name: &str) -> Result<(), DriverError> {
149 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
150 self.request::<Value>("POST", &format!("/_api/database/{}/collection", db), Some(&serde_json::json!({"name": name}))).await?;
151 Ok(())
152 }
153
154 pub async fn insert(&self, collection: &str, document: Value, key: Option<&str>) -> Result<Value, DriverError> {
155 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
156 let mut doc = document;
157 if let Some(k) = key {
158 if let Some(obj) = doc.as_object_mut() {
159 obj.insert("_key".to_string(), serde_json::json!(k));
160 }
161 }
162 let path = format!("/_api/database/{}/document/{}", db, collection);
163 self.client
164 .post(&format!("{}{}", self.base_url, path))
165 .headers(self.get_headers())
166 .json(&doc)
167 .send()
168 .await
169 .map_err(|e| DriverError::ConnectionError(format!("HTTP request failed: {}", e)))?
170 .json()
171 .await
172 .map_err(|e| DriverError::ProtocolError(format!("Failed to parse response: {}", e)))
173 }
174
175 pub async fn get(&self, collection: &str, key: &str) -> Result<Option<Value>, DriverError> {
176 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
177 let path = format!("/_api/database/{}/document/{}/{}", db, collection, key);
178 let response: Value = self
179 .client
180 .get(&format!("{}{}", self.base_url, path))
181 .headers(self.get_headers())
182 .send()
183 .await
184 .map_err(|e| DriverError::ConnectionError(format!("HTTP request failed: {}", e)))?
185 .json()
186 .await
187 .map_err(|e| DriverError::ProtocolError(format!("Failed to parse response: {}", e)))?;
188 Ok(Some(response))
189 }
190
191 pub async fn update(&self, collection: &str, key: &str, document: Value, merge: bool) -> Result<(), DriverError> {
192 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
193 let payload = serde_json::json!({
194 "document": document,
195 "merge": merge
196 });
197 let path = format!("/_api/database/{}/document/{}/{}", db, collection, key);
198 self.request::<Value>("PUT", &path, Some(&payload)).await?;
199 Ok(())
200 }
201
202 pub async fn delete(&self, collection: &str, key: &str) -> Result<(), DriverError> {
203 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
204 let path = format!("/_api/database/{}/collection/{}/document/{}", db, collection, key);
205 self.request::<Value>("DELETE", &path, None).await?;
206 Ok(())
207 }
208
209 pub async fn list(&self, collection: &str, limit: i32, offset: i32) -> Result<Vec<Value>, DriverError> {
210 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
211 let path = format!("/_api/database/{}/collection/{}/documents?limit={}&offset={}", db, collection, limit, offset);
212 let response: Value = self.request("GET", &path, None).await?;
213 Ok(response.get("documents")
214 .and_then(|d| d.as_array())
215 .cloned()
216 .unwrap_or_default())
217 }
218
219 pub async fn query(&self, sdbql: &str, bind_vars: Option<Value>) -> Result<Vec<Value>, DriverError> {
220 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
221 let mut payload = serde_json::json!({
222 "database": db,
223 "sdbql": sdbql
224 });
225 if let Some(bv) = bind_vars {
226 payload["bind_vars"] = bv;
227 }
228 let response: Value = self.request("POST", "/_api/query", Some(&payload)).await?;
229 Ok(response.get("result")
230 .and_then(|r| r.as_array())
231 .cloned()
232 .unwrap_or_default())
233 }
234
235 pub async fn begin_transaction(&self, isolation_level: Option<&str>) -> Result<String, DriverError> {
236 let db = self.database.as_deref().ok_or_else(|| DriverError::ProtocolError("No database specified".to_string()))?;
237 let mut payload = serde_json::json!({
238 "database": db
239 });
240 if let Some(il) = isolation_level {
241 payload["isolation_level"] = serde_json::json!(il);
242 }
243 let response: Value = self.request("POST", "/_api/transaction/begin", Some(&payload)).await?;
244 response.get("tx_id")
245 .and_then(|t| t.as_str())
246 .map(|s| s.to_string())
247 .ok_or_else(|| DriverError::ProtocolError("No tx_id in response".to_string()))
248 }
249
250 pub async fn commit_transaction(&self, tx_id: &str) -> Result<(), DriverError> {
251 self.request::<Value>("POST", "/_api/transaction/commit", Some(&serde_json::json!({"tx_id": tx_id}))).await?;
252 Ok(())
253 }
254
255 pub async fn rollback_transaction(&self, tx_id: &str) -> Result<(), DriverError> {
256 self.request::<Value>("POST", "/_api/transaction/rollback", Some(&serde_json::json!({"tx_id": tx_id}))).await?;
257 Ok(())
258 }
259
260 pub async fn cluster_status(&self) -> Result<Value, DriverError> {
261 self.request("GET", "/_api/cluster/status", None).await
262 }
263
264 pub async fn cluster_info(&self) -> Result<Value, DriverError> {
265 self.request("GET", "/_api/cluster/info", None).await
266 }
267
268 pub async fn ping(&self) -> Result<bool, DriverError> {
269 let response = self.request::<Value>("GET", "/health", None).await;
270 Ok(response.is_ok())
271 }
272}