rs_puff/
responses.rs

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}