1use serde::Deserialize;
2use std::collections::HashMap;
3
4use crate::Row;
5
6#[derive(Debug, Clone, Deserialize)]
7pub struct WriteResponse {
8 pub rows_affected: u64,
9
10 #[serde(default)]
11 pub rows_upserted: Option<u64>,
12
13 #[serde(default)]
14 pub rows_patched: Option<u64>,
15
16 #[serde(default)]
17 pub rows_deleted: Option<u64>,
18
19 #[serde(default)]
20 pub rows_remaining: Option<bool>,
21
22 #[serde(default)]
23 pub upserted_ids: Option<Vec<serde_json::Value>>,
24
25 #[serde(default)]
26 pub patched_ids: Option<Vec<serde_json::Value>>,
27
28 #[serde(default)]
29 pub deleted_ids: Option<Vec<serde_json::Value>>,
30
31 #[serde(default)]
32 pub billing: Option<WriteBilling>,
33}
34
35#[derive(Debug, Clone, Deserialize)]
36pub struct WriteBilling {
37 pub billable_logical_bytes_written: u64,
38
39 #[serde(default)]
40 pub query: Option<QueryBillingInfo>,
41}
42
43#[derive(Debug, Clone, Deserialize)]
44pub struct QueryBillingInfo {
45 pub billable_logical_bytes_queried: u64,
46 pub billable_logical_bytes_returned: u64,
47}
48
49#[derive(Debug, Clone, Deserialize)]
50pub struct QueryResponse {
51 #[serde(default)]
52 pub rows: Vec<Row>,
53
54 #[serde(default)]
55 pub aggregations: Option<HashMap<String, serde_json::Value>>,
56
57 #[serde(default)]
58 pub aggregation_groups: Option<Vec<HashMap<String, serde_json::Value>>>,
59
60 #[serde(default)]
61 pub billing: Option<QueryBilling>,
62
63 #[serde(default)]
64 pub performance: Option<QueryPerformance>,
65}
66
67#[derive(Debug, Clone, Deserialize)]
68pub struct QueryBilling {
69 pub billable_logical_bytes_queried: u64,
70 pub billable_logical_bytes_returned: u64,
71}
72
73#[derive(Debug, Clone, Deserialize)]
74pub struct QueryPerformance {
75 #[serde(default)]
76 pub cache_hit_ratio: Option<f64>,
77
78 #[serde(default)]
79 pub cache_temperature: Option<String>,
80
81 #[serde(default)]
82 pub server_total_ms: Option<u64>,
83
84 #[serde(default)]
85 pub query_execution_ms: Option<u64>,
86
87 #[serde(default)]
88 pub exhaustive_search_count: Option<u64>,
89
90 #[serde(default)]
91 pub approx_namespace_size: Option<u64>,
92}
93
94#[derive(Debug, Clone, Deserialize)]
95pub struct MultiQueryResponse {
96 pub results: Vec<QueryResponse>,
97}
98
99#[derive(Debug, Clone, Deserialize)]
100pub struct DeleteAllResponse {
101 pub status: String,
102}
103
104#[derive(Debug, Clone, Deserialize)]
105pub struct NamespaceMetadata {
106 #[serde(default)]
107 pub created_at: Option<String>,
108
109 #[serde(default)]
110 pub updated_at: Option<String>,
111
112 #[serde(default)]
113 pub approx_logical_bytes: Option<u64>,
114
115 #[serde(default)]
116 pub approx_row_count: Option<u64>,
117
118 #[serde(default)]
119 pub encryption: Option<NamespaceEncryption>,
120
121 #[serde(default)]
122 pub index: Option<NamespaceIndex>,
123
124 #[serde(default)]
125 pub schema: Option<HashMap<String, serde_json::Value>>,
126}
127
128#[derive(Debug, Clone, Deserialize)]
129pub struct NamespaceEncryption {
130 #[serde(default)]
131 pub sse: Option<bool>,
132
133 #[serde(default)]
134 pub cmek: Option<serde_json::Value>,
135}
136
137#[derive(Debug, Clone, Deserialize)]
138pub struct NamespaceIndex {
139 #[serde(default)]
140 pub status: Option<String>,
141}
142
143#[derive(Debug, Clone, Deserialize)]
144pub struct SchemaResponse(pub HashMap<String, serde_json::Value>);
145
146#[derive(Debug, Clone, Deserialize)]
147pub struct HintCacheWarmResponse {
148 pub status: String,
149
150 #[serde(default)]
151 pub message: Option<String>,
152}
153
154#[derive(Debug, Clone, Deserialize)]
155pub struct NamespaceSummary {
156 pub id: String,
157}
158
159#[derive(Debug, Clone, Deserialize)]
160pub struct NamespacesResponse {
161 pub namespaces: Vec<NamespaceSummary>,
162
163 #[serde(default)]
164 pub next_cursor: Option<String>,
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_write_response_deserialization() {
173 let json = r#"{"rows_affected": 5}"#;
174 let resp: WriteResponse = serde_json::from_str(json).unwrap();
175 assert_eq!(resp.rows_affected, 5);
176 assert!(resp.rows_upserted.is_none());
177 }
178
179 #[test]
180 fn test_write_response_full() {
181 let json = r#"{
182 "rows_affected": 10,
183 "rows_upserted": 5,
184 "rows_patched": 3,
185 "rows_deleted": 2,
186 "upserted_ids": [1, 2, 3, 4, 5],
187 "billing": {
188 "billable_logical_bytes_written": 1024
189 }
190 }"#;
191 let resp: WriteResponse = serde_json::from_str(json).unwrap();
192 assert_eq!(resp.rows_affected, 10);
193 assert_eq!(resp.rows_upserted, Some(5));
194 assert_eq!(resp.rows_patched, Some(3));
195 assert_eq!(resp.rows_deleted, Some(2));
196 assert_eq!(resp.upserted_ids.as_ref().unwrap().len(), 5);
197 assert!(resp.billing.is_some());
198 }
199
200 #[test]
201 fn test_query_response_deserialization() {
202 let json = r#"{
203 "rows": [
204 {"id": 1, "name": "alice", "_dist": 0.1},
205 {"id": 2, "name": "bob", "_dist": 0.2}
206 ]
207 }"#;
208 let resp: QueryResponse = serde_json::from_str(json).unwrap();
209 assert_eq!(resp.rows.len(), 2);
210 }
211
212 #[test]
213 fn test_query_response_with_aggregations() {
214 let json = r#"{
215 "rows": [],
216 "aggregations": {"count": 42, "avg_score": 0.75}
217 }"#;
218 let resp: QueryResponse = serde_json::from_str(json).unwrap();
219 assert!(resp.aggregations.is_some());
220 let aggs = resp.aggregations.unwrap();
221 assert_eq!(aggs.get("count").unwrap(), 42);
222 }
223
224 #[test]
225 fn test_query_response_with_performance() {
226 let json = r#"{
227 "rows": [],
228 "performance": {
229 "cache_hit_ratio": 0.95,
230 "cache_temperature": "hot",
231 "server_total_ms": 10,
232 "query_execution_ms": 5
233 }
234 }"#;
235 let resp: QueryResponse = serde_json::from_str(json).unwrap();
236 let perf = resp.performance.unwrap();
237 assert_eq!(perf.cache_hit_ratio, Some(0.95));
238 assert_eq!(perf.cache_temperature, Some("hot".to_string()));
239 }
240
241 #[test]
242 fn test_delete_all_response() {
243 let json = r#"{"status": "ok"}"#;
244 let resp: DeleteAllResponse = serde_json::from_str(json).unwrap();
245 assert_eq!(resp.status, "ok");
246 }
247
248 #[test]
249 fn test_namespace_metadata() {
250 let json = r#"{
251 "created_at": "2024-01-15T12:00:00Z",
252 "updated_at": "2024-01-15T12:30:00Z",
253 "approx_logical_bytes": 1024,
254 "approx_row_count": 100,
255 "encryption": { "sse": true },
256 "index": { "status": "up-to-date" },
257 "schema": { "id": { "type": "uint" } }
258 }"#;
259 let resp: NamespaceMetadata = serde_json::from_str(json).unwrap();
260 assert_eq!(resp.created_at, Some("2024-01-15T12:00:00Z".to_string()));
261 assert_eq!(resp.approx_row_count, Some(100));
262 assert!(resp.encryption.is_some());
263 assert_eq!(resp.encryption.unwrap().sse, Some(true));
264 }
265
266 #[test]
267 fn test_namespaces_response() {
268 let json = r#"{
269 "namespaces": [
270 {"id": "ns1"},
271 {"id": "ns2"}
272 ],
273 "next_cursor": "abc123"
274 }"#;
275 let resp: NamespacesResponse = serde_json::from_str(json).unwrap();
276 assert_eq!(resp.namespaces.len(), 2);
277 assert_eq!(resp.namespaces[0].id, "ns1");
278 assert_eq!(resp.next_cursor, Some("abc123".to_string()));
279 }
280
281 #[test]
282 fn test_multi_query_response() {
283 let json = r#"{
284 "results": [
285 {"rows": [{"id": 1}]},
286 {"rows": [{"id": 2}, {"id": 3}]}
287 ]
288 }"#;
289 let resp: MultiQueryResponse = serde_json::from_str(json).unwrap();
290 assert_eq!(resp.results.len(), 2);
291 assert_eq!(resp.results[0].rows.len(), 1);
292 assert_eq!(resp.results[1].rows.len(), 2);
293 }
294}