aether_protocol/
lib.rs

1// File: src/lib.rs
2// =============================================================================
3// This is the main library file. Its primary job is to declare the modules
4// and re-export their contents so they are easily accessible to any crate
5// that uses `aether-protocol`.
6
7//! # AetherDB Network Protocol
8//!
9//! This crate defines the official, stable network protocol for communicating
10//! with an AetherDB instance. It contains all request and response data
11//! structures, serialized using `bincode` for maximum performance.
12
13// Declare the modules that make up our library.
14pub mod request;
15pub mod response;
16pub mod types;
17
18// Re-export the most important structs and enums for convenience.
19pub use request::Request;
20pub use response::Response;
21pub use types::{BatchRequest, BatchResponse, DbStats, Direction, Filter, QueryOptions, Record, RecordSet};
22pub use response::QueryMetrics;
23
24#[cfg(test)]
25mod tests {
26    use crate::types::{BatchRequest, BatchResponse, DbStats, Direction, Filter, QueryOptions, Record, RecordSet};
27    use crate::{Request, Response};
28    use serde_json::json;
29    use std::collections::HashMap;
30
31    // Helper functions to test serialization/deserialization roundtrip
32    
33    // Use serde_json for testing since it handles serde_json::Value better than bincode
34    fn test_serialization_json<T: serde::Serialize + serde::de::DeserializeOwned + PartialEq + std::fmt::Debug>(
35        value: T,
36    ) -> T {
37        let serialized = serde_json::to_string(&value).expect("Failed to serialize to JSON");
38        let deserialized = serde_json::from_str(&serialized).expect("Failed to deserialize from JSON");
39        assert_eq!(value, deserialized, "Data loss during JSON serialization roundtrip");
40        deserialized
41    }
42    
43    // For non-JSON-Value types, we can still use bincode to ensure it works
44    fn test_serialization_bincode<T: serde::Serialize + serde::de::DeserializeOwned + PartialEq + std::fmt::Debug>(
45        value: T,
46    ) -> T {
47        let serialized = bincode::serialize(&value).expect("Failed to serialize");
48        let deserialized = bincode::deserialize(&serialized).expect("Failed to deserialize");
49        assert_eq!(value, deserialized, "Data loss during bincode serialization roundtrip");
50        deserialized
51    }
52
53    #[test]
54    fn test_record_serialization() {
55        let mut record = Record::new();
56        record.insert("name".to_string(), json!("John Doe"));
57        record.insert("age".to_string(), json!(30));
58        record.insert("active".to_string(), json!(true));
59        record.insert("scores".to_string(), json!([85, 90, 78]));
60        
61        let deserialized = test_serialization_json(record);
62        assert_eq!(deserialized["name"], json!("John Doe"));
63    }
64
65    #[test]
66    fn test_recordset_serialization() {
67        let mut record1 = Record::new();
68        record1.insert("id".to_string(), json!(1));
69        record1.insert("name".to_string(), json!("Record 1"));
70        
71        let mut record2 = Record::new();
72        record2.insert("id".to_string(), json!(2));
73        record2.insert("name".to_string(), json!("Record 2"));
74        
75        let recordset = RecordSet {
76            records: vec![record1, record2],
77        };
78        
79        test_serialization_json(recordset);
80    }
81
82    #[test]
83    fn test_filter_serialization() {
84        // Test each Filter variant
85        let filters = vec![
86            Filter::Equals {
87                field: "status".to_string(),
88                value: json!("active"),
89            },
90            Filter::NotEquals {
91                field: "deleted".to_string(),
92                value: json!(true),
93            },
94            Filter::GreaterThan {
95                field: "age".to_string(),
96                value: 18.0,
97            },
98            Filter::LessThan {
99                field: "price".to_string(),
100                value: 100.0,
101            },
102            Filter::In {
103                field: "category".to_string(),
104                values: vec![json!("electronics"), json!("books")],
105            },
106            Filter::And(vec![
107                Filter::Equals {
108                    field: "active".to_string(),
109                    value: json!(true),
110                },
111                Filter::GreaterThan {
112                    field: "score".to_string(),
113                    value: 70.0,
114                },
115            ]),
116            Filter::Or(vec![
117                Filter::Equals {
118                    field: "type".to_string(),
119                    value: json!("premium"),
120                },
121                Filter::Equals {
122                    field: "special".to_string(),
123                    value: json!(true),
124                },
125            ]),
126        ];
127        
128        for filter in filters {
129            test_serialization_json(filter);
130        }
131    }
132
133    #[test]
134    fn test_query_options_serialization() {
135        let options = QueryOptions {
136            sort_by: Some(("created_at".to_string(), Direction::Desc)),
137            limit: Some(100),
138            offset: Some(20),
139        };
140        
141        // Can use bincode for this since it doesn't have serde_json::Value
142        test_serialization_bincode(options);
143    }
144
145    #[test]
146    fn test_db_stats_serialization() {
147        let stats = DbStats {
148            collection_count: 5,
149            record_count: 1000,
150        };
151        
152        // Can use bincode for this since it doesn't have serde_json::Value
153        test_serialization_bincode(stats);
154    }
155
156    #[test]
157    fn test_batch_request_serialization() {
158        let mut requests = HashMap::new();
159        requests.insert("key1".to_string(), ("testdb".to_string(), "users".to_string(), "user_1".to_string()));
160        requests.insert("key2".to_string(), ("testdb".to_string(), "products".to_string(), "product_1".to_string()));
161        
162        let batch_request = BatchRequest { requests };
163        // Can use bincode for this since it doesn't have serde_json::Value
164        test_serialization_bincode(batch_request);
165    }
166
167    #[test]
168    fn test_batch_response_serialization() {
169        let mut record1 = Record::new();
170        record1.insert("id".to_string(), json!("user_1"));
171        record1.insert("name".to_string(), json!("John Doe"));
172        
173        let mut record2 = Record::new();
174        record2.insert("id".to_string(), json!("product_1"));
175        record2.insert("name".to_string(), json!("Widget"));
176        
177        let mut results = HashMap::new();
178        results.insert("key1".to_string(), Some(record1));
179        results.insert("key2".to_string(), Some(record2));
180        results.insert("key3".to_string(), None); // Test None case
181        
182        let batch_response = BatchResponse { results };
183        test_serialization_json(batch_response);
184    }
185
186    #[test]
187    fn test_request_serialization() {
188        // Test all Request variants
189        let requests = vec![
190            // Database Management
191            Request::CreateDatabase { db_name: "testdb".to_string() },
192            Request::DropDatabase { db_name: "testdb".to_string() },
193            Request::ListDatabases,
194            
195            // Collection Management
196            Request::ListCollections,
197            Request::CreateCollection { db_name: "users".to_string(), collection_name: "users".to_string() },
198            Request::DropCollection { db_name: "users".to_string(), collection_name: "users".to_string() },
199            Request::GetStats,
200            Request::Flush,
201            
202            // Index Management
203            Request::CreateIndex {
204                db_name: "users".to_string(),
205                collection: "users".to_string(),
206                field_name: "email".to_string(),
207            },
208            Request::DropIndex {
209                db_name: "users".to_string(),
210                collection: "users".to_string(),
211                field_name: "email".to_string(),
212            },
213            Request::ListIndexes {
214                db_name: "users".to_string(),
215                collection: "users".to_string(),
216            },
217            
218            // CRUD Operations
219            Request::CreateRecord {
220                db_name: "users".to_string(),
221                collection: "users".to_string(),
222                record_id: "user123".to_string(),
223                data: {
224                    let mut record = Record::new();
225                    record.insert("name".to_string(), json!("Alice"));
226                    record.insert("email".to_string(), json!("alice@example.com"));
227                    record
228                },
229            },
230            Request::UpdateRecord {
231                db_name: "users".to_string(),
232                collection: "users".to_string(),
233                record_id: "user123".to_string(),
234                data: {
235                    let mut record = Record::new();
236                    record.insert("active".to_string(), json!(false));
237                    record
238                },
239            },
240            Request::UpsertRecord {
241                db_name: "users".to_string(),
242                collection: "users".to_string(),
243                record_id: "user123".to_string(),
244                data: {
245                    let mut record = Record::new();
246                    record.insert("name".to_string(), json!("Alice"));
247                    record.insert("email".to_string(), json!("updated@example.com"));
248                    record
249                },
250            },
251            Request::GetRecord {
252                db_name: "users".to_string(),
253                collection: "users".to_string(),
254                record_id: "user123".to_string(),
255            },
256            Request::DeleteRecord {
257                db_name: "users".to_string(),
258                collection: "users".to_string(),
259                record_id: "user123".to_string(),
260                cascade: true,
261            },
262            Request::GetLastInsertId,
263            
264            // Querying & Relational
265            Request::FindRecords {
266                db_name: "users".to_string(),
267                collection: "users".to_string(),
268                filter: crate::types::Filter::And(vec![
269                    crate::types::Filter::Equals {
270                        field: "active".to_string(),
271                        value: json!(true),
272                    },
273                    crate::types::Filter::GreaterThan {
274                        field: "age".to_string(),
275                        value: 21.0,
276                    },
277                ]),
278                options: Some(crate::types::QueryOptions {
279                    sort_by: Some(("created_at".to_string(), crate::types::Direction::Desc)),
280                    limit: Some(50),
281                    offset: Some(0),
282                }),
283            },
284            Request::CountRecords {
285                db_name: "users".to_string(),
286                collection: "users".to_string(),
287                filter: crate::types::Filter::Equals {
288                    field: "active".to_string(),
289                    value: json!(true),
290                },
291            },
292            Request::GetRecordWithRelated {
293                db_name: "users".to_string(),
294                primary_collection: "orders".to_string(),
295                primary_record_id: "order123".to_string(),
296                relation_key_field: "user_id".to_string(),
297                related_collection: "users".to_string(),
298            },
299            Request::ExecuteBatchGet({
300                let mut requests = HashMap::new();
301                requests.insert("key1".to_string(), ("testdb".to_string(), "users".to_string(), "user123".to_string()));
302                requests.insert("key2".to_string(), ("testdb".to_string(), "products".to_string(), "product456".to_string()));
303                crate::types::BatchRequest { requests }
304            }),
305            Request::Search {
306                db_name: "users".to_string(),
307                collection: "users".to_string(),
308                query: "John Doe".to_string(),
309                field: Some("name".to_string()),
310            },
311            Request::Search {
312                db_name: "users".to_string(),
313                collection: "users".to_string(),
314                query: "John Doe".to_string(),
315                field: None, // The field is absent
316            },
317        ];
318        
319        for request in requests {
320            test_serialization_json(request);
321        }
322    }
323
324    #[test]
325    fn test_response_serialization() {
326        // Test all Response variants
327        let responses = vec![
328            // General Responses
329            Response::Success,
330            Response::Error("Invalid request format".to_string()),
331            
332            // Database Management Responses
333            Response::DatabaseList(vec![
334                "testdb".to_string(),
335                "userdb".to_string(),
336                "analytics".to_string(),
337            ]),
338            Response::DatabaseCreated(true),
339            Response::DatabaseDropped(true),
340            
341            // Collection Management Responses
342            Response::CollectionList(vec![
343                "users".to_string(),
344                "products".to_string(),
345                "orders".to_string(),
346            ]),
347            Response::Stats(crate::types::DbStats {
348                collection_count: 3,
349                record_count: 1500,
350            }),
351            Response::IndexList(vec![
352                "email".to_string(),
353                "username".to_string(),
354            ]),
355            
356            // Record & Query Responses
357            Response::Record(Some({
358                let mut record = Record::new();
359                record.insert("id".to_string(), json!("user123"));
360                record.insert("name".to_string(), json!("Bob"));
361                record.insert("email".to_string(), json!("bob@example.com"));
362                record
363            })),
364            Response::Record(None), // Test None case
365            Response::RecordSet(crate::types::RecordSet {
366                records: vec![
367                    {
368                        let mut record = Record::new();
369                        record.insert("id".to_string(), json!("1"));
370                        record.insert("name".to_string(), json!("Item 1"));
371                        record
372                    },
373                    {
374                        let mut record = Record::new();
375                        record.insert("id".to_string(), json!("2"));
376                        record.insert("name".to_string(), json!("Item 2"));
377                        record
378                    },
379                ],
380            }),
381            Response::RecordCount(42),
382            Response::RecordDeleted(true),
383            Response::LastInsertId(123),
384            Response::RecordWithRelated(Some(({
385                let mut order = Record::new();
386                order.insert("id".to_string(), json!("order123"));
387                order.insert("amount".to_string(), json!(99.99));
388                order
389            }, {
390                let mut user = Record::new();
391                user.insert("id".to_string(), json!("user456"));
392                user.insert("name".to_string(), json!("Charlie"));
393                user
394            }))),
395            Response::RecordWithRelated(None), // Test None case
396            Response::BatchResponse({
397                let mut results = HashMap::new();
398                let mut user_record = Record::new();
399                user_record.insert("id".to_string(), json!("user123"));
400                user_record.insert("name".to_string(), json!("Dave"));
401                
402                let mut product_record = Record::new();
403                product_record.insert("id".to_string(), json!("product456"));
404                product_record.insert("name".to_string(), json!("Gadget"));
405                
406                results.insert("key1".to_string(), Some(user_record));
407                results.insert("key2".to_string(), Some(product_record));
408                results.insert("key3".to_string(), None); // Test None case
409                
410                crate::types::BatchResponse { results }
411            }),
412        ];
413        
414        for response in responses {
415            test_serialization_json(response);
416        }
417    }
418}
419#[test]
420fn test_result_metrics_serialization() {
421    // 1. Create the inner data (the actual result of a query).
422    let record_set = RecordSet { records: vec![] };
423    let inner_response = Response::RecordSet(record_set);
424
425    // 2. Create the metrics data.
426    let metrics = QueryMetrics {
427        execution_time_micros: 12345,
428    };
429
430    // 3. Wrap them in the new ResultMetrics response.
431    let original_response = Response::ResultMetrics {
432        data: Box::new(inner_response),
433        metrics,
434    };
435
436    // 4. Serialize and deserialize the response.
437    let bytes = bincode::serialize(&original_response).unwrap();
438    let deserialized_response: Response = bincode::deserialize(&bytes).unwrap();
439
440    // 5. Assert that the data survived the round trip perfectly.
441    assert_eq!(original_response, deserialized_response);
442}