1use reddb_grpc_proto::red_db_client::RedDbClient;
14use reddb_grpc_proto::*;
15use tonic::transport::Channel;
16use tonic::Request;
17
18#[derive(Debug, Clone)]
19pub struct HealthStatus {
20 pub healthy: bool,
21 pub state: String,
22 pub checked_at_unix_ms: u64,
23}
24
25#[derive(Debug, Clone)]
26pub struct QueryResponse {
27 pub ok: bool,
28 pub mode: String,
29 pub statement: String,
30 pub engine: String,
31 pub columns: Vec<String>,
32 pub record_count: u64,
33 pub affected_rows: u64,
36 pub result_json: String,
37}
38
39#[derive(Debug, Clone)]
40pub struct CreatedEntity {
41 pub ok: bool,
42 pub id: u64,
43 pub entity_json: String,
44}
45
46#[derive(Debug, Clone)]
47pub struct BulkCreateStatus {
48 pub ok: bool,
49 pub count: u64,
50 pub ids: Vec<u64>,
51}
52
53#[derive(Debug, Clone)]
54pub struct OperationStatus {
55 pub ok: bool,
56 pub message: String,
57}
58
59#[derive(Clone)]
60pub struct RedDBClient {
61 inner: RedDbClient<Channel>,
62 token: Option<String>,
63 pub addr: String,
64}
65
66impl RedDBClient {
67 pub async fn connect(
68 addr: &str,
69 token: Option<String>,
70 ) -> Result<Self, Box<dyn std::error::Error>> {
71 let endpoint = if addr.starts_with("http") {
72 addr.to_string()
73 } else {
74 format!("http://{}", addr)
75 };
76 let inner = RedDbClient::connect(endpoint.clone()).await?;
77 Ok(Self {
78 inner,
79 token,
80 addr: endpoint,
81 })
82 }
83
84 fn auth_request<T>(&self, inner: T) -> Request<T> {
85 let mut req = Request::new(inner);
86 if let Some(ref token) = self.token {
87 if let Ok(value) = format!("Bearer {}", token).parse() {
88 req.metadata_mut().insert("authorization", value);
89 }
90 }
91 req
92 }
93
94 pub fn set_token(&mut self, token: String) {
96 self.token = Some(token);
97 }
98
99 pub async fn health_status(&mut self) -> Result<HealthStatus, Box<dyn std::error::Error>> {
100 let req = self.auth_request(Empty {});
101 let resp = self.inner.health(req).await?;
102 let reply = resp.into_inner();
103 Ok(HealthStatus {
104 healthy: reply.healthy,
105 state: reply.state,
106 checked_at_unix_ms: reply.checked_at_unix_ms,
107 })
108 }
109
110 pub async fn health(&mut self) -> Result<String, Box<dyn std::error::Error>> {
111 let reply = self.health_status().await?;
112 Ok(format!(
113 "state: {}, healthy: {}",
114 reply.state, reply.healthy
115 ))
116 }
117
118 pub async fn query_reply(
119 &mut self,
120 sql: &str,
121 ) -> Result<QueryResponse, Box<dyn std::error::Error>> {
122 self.query_reply_with_params(sql, Vec::new()).await
123 }
124
125 pub async fn query_reply_with_params(
126 &mut self,
127 sql: &str,
128 params: Vec<QueryValue>,
129 ) -> Result<QueryResponse, Box<dyn std::error::Error>> {
130 let req = self.auth_request(QueryRequest {
131 query: sql.to_string(),
132 entity_types: vec![],
133 capabilities: vec![],
134 params,
135 });
136 let resp = self.inner.query(req).await?;
137 let reply = resp.into_inner();
138 Ok(QueryResponse {
139 ok: reply.ok,
140 mode: reply.mode,
141 statement: reply.statement,
142 engine: reply.engine,
143 columns: reply.columns,
144 record_count: reply.record_count,
145 affected_rows: reply.affected_rows,
146 result_json: reply.result_json,
147 })
148 }
149
150 pub async fn query(&mut self, sql: &str) -> Result<String, Box<dyn std::error::Error>> {
151 let reply = self.query_reply(sql).await?;
152 Ok(reply.result_json)
153 }
154
155 pub async fn collections(&mut self) -> Result<Vec<String>, Box<dyn std::error::Error>> {
156 let req = self.auth_request(Empty {});
157 let resp = self.inner.collections(req).await?;
158 Ok(resp.into_inner().collections)
159 }
160
161 pub async fn scan(
162 &mut self,
163 collection: &str,
164 limit: u64,
165 ) -> Result<String, Box<dyn std::error::Error>> {
166 let req = self.auth_request(ScanRequest {
167 collection: collection.to_string(),
168 offset: 0,
169 limit,
170 });
171 let resp = self.inner.scan(req).await?;
172 let reply = resp.into_inner();
173 let items: Vec<String> = reply.items.iter().map(|e| e.json.clone()).collect();
174 Ok(format!(
175 "total: {}, items: [{}]",
176 reply.total,
177 items.join(", ")
178 ))
179 }
180
181 pub async fn stats(&mut self) -> Result<String, Box<dyn std::error::Error>> {
182 let req = self.auth_request(Empty {});
183 let resp = self.inner.stats(req).await?;
184 let reply = resp.into_inner();
185 Ok(format!(
186 "collections: {}, entities: {}, memory: {} bytes, started_at: {}",
187 reply.collection_count,
188 reply.total_entities,
189 reply.total_memory_bytes,
190 reply.started_at_unix_ms
191 ))
192 }
193
194 pub async fn create_row(
195 &mut self,
196 collection: &str,
197 json: &str,
198 ) -> Result<String, Box<dyn std::error::Error>> {
199 let reply = self.create_row_entity(collection, json).await?;
200 Ok(format!("id: {}, entity: {}", reply.id, reply.entity_json))
201 }
202
203 pub async fn create_row_entity(
204 &mut self,
205 collection: &str,
206 json: &str,
207 ) -> Result<CreatedEntity, Box<dyn std::error::Error>> {
208 let req = self.auth_request(JsonCreateRequest {
209 collection: collection.to_string(),
210 payload_json: json.to_string(),
211 });
212 let resp = self.inner.create_row(req).await?;
213 let reply = resp.into_inner();
214 Ok(CreatedEntity {
215 ok: reply.ok,
216 id: reply.id,
217 entity_json: reply.entity_json,
218 })
219 }
220
221 pub async fn bulk_create_rows(
222 &mut self,
223 collection: &str,
224 payload_json: Vec<String>,
225 ) -> Result<BulkCreateStatus, Box<dyn std::error::Error>> {
226 let req = self.auth_request(JsonBulkCreateRequest {
227 collection: collection.to_string(),
228 payload_json,
229 });
230 let resp = self.inner.bulk_create_rows(req).await?;
231 let reply = resp.into_inner();
232 Ok(BulkCreateStatus {
233 ok: reply.ok,
234 count: reply.count,
235 ids: reply.items.into_iter().map(|item| item.id).collect(),
236 })
237 }
238
239 pub async fn explain(&mut self, sql: &str) -> Result<String, Box<dyn std::error::Error>> {
240 let req = self.auth_request(QueryRequest {
241 query: sql.to_string(),
242 entity_types: vec![],
243 capabilities: vec![],
244 params: vec![],
245 });
246 let resp = self.inner.explain_query(req).await?;
247 Ok(resp.into_inner().payload)
248 }
249
250 pub async fn login(
251 &mut self,
252 username: &str,
253 password: &str,
254 ) -> Result<String, Box<dyn std::error::Error>> {
255 let payload = format!(
256 "{{\"username\":\"{}\",\"password\":\"{}\"}}",
257 username, password
258 );
259 let req = self.auth_request(JsonPayloadRequest {
260 payload_json: payload,
261 });
262 let resp = self.inner.auth_login(req).await?;
263 let reply = resp.into_inner();
264 Ok(reply.payload)
265 }
266
267 pub async fn replication_status(&mut self) -> Result<String, Box<dyn std::error::Error>> {
268 let req = self.auth_request(Empty {});
269 let resp = self.inner.replication_status(req).await?;
270 Ok(resp.into_inner().payload)
271 }
272
273 pub async fn topology(&mut self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
279 let req = self.auth_request(TopologyRequest {});
280 let resp = self.inner.topology(req).await?;
281 Ok(resp.into_inner().topology_bytes)
282 }
283
284 pub async fn delete_entity(
285 &mut self,
286 collection: &str,
287 id: u64,
288 ) -> Result<OperationStatus, Box<dyn std::error::Error>> {
289 let req = self.auth_request(DeleteEntityRequest {
290 collection: collection.to_string(),
291 id,
292 });
293 let resp = self.inner.delete_entity(req).await?;
294 let reply = resp.into_inner();
295 Ok(OperationStatus {
296 ok: reply.ok,
297 message: reply.message,
298 })
299 }
300}