Skip to main content

ormdb_proto/
lib.rs

1//! ORMDB Protocol types and serialization.
2//!
3//! This crate defines the wire protocol types for ORMDB, using rkyv for
4//! zero-copy serialization.
5//!
6//! # Modules
7//!
8//! - [`value`] - Runtime value types for query parameters and results
9//! - [`query`] - Query IR types for graph queries
10//! - [`mutation`] - Mutation types for write operations
11//! - [`result`] - Result types for query responses
12//! - [`message`] - Request/response message wrappers
13//! - [`handshake`] - Protocol negotiation types
14//! - [`error`] - Protocol error types
15//!
16//! # Serialization
17//!
18//! All types in this crate derive `rkyv::Archive`, `rkyv::Serialize`, and
19//! `rkyv::Deserialize`. Use rkyv directly for serialization:
20//!
21//! ```ignore
22//! use ormdb_proto::{Value, GraphQuery};
23//!
24//! // Serialize
25//! let value = Value::String("hello".into());
26//! let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&value).unwrap();
27//!
28//! // Deserialize
29//! let archived = rkyv::access::<ArchivedValue, rkyv::rancor::Error>(&bytes).unwrap();
30//! let deserialized: Value = rkyv::deserialize::<Value, rkyv::rancor::Error>(archived).unwrap();
31//! ```
32
33pub mod error;
34pub mod explain;
35pub mod framing;
36pub mod handshake;
37pub mod message;
38pub mod metrics;
39pub mod mutation;
40pub mod query;
41pub mod replication;
42pub mod result;
43pub mod value;
44
45pub use error::Error;
46
47// Re-export commonly used types at crate root
48pub use explain::{
49    BudgetSummary, CostSummary, ExplainResult, IncludeSummary, JoinInfo, JoinStrategyType,
50    OrderSummary, PaginationSummary, QueryPlanSummary,
51};
52pub use handshake::{Handshake, HandshakeResponse};
53pub use message::{
54    error_codes, ChangeEvent, ChangeType, Operation, Request, Response, ResponsePayload, Status,
55    Subscription,
56};
57pub use metrics::{
58    CacheMetrics, EntityCount, EntityQueryCount, MetricsResult, MutationMetrics, QueryMetrics,
59    StorageMetrics, TransportMetrics,
60};
61pub use mutation::{FieldValue, Mutation, MutationBatch};
62pub use query::{
63    AggregateFunction, AggregateQuery, Aggregation, Filter, FilterExpr, GraphQuery, OrderDirection,
64    OrderSpec, Pagination, RelationInclude, SimpleFilter,
65};
66pub use replication::{
67    ChangeLogEntry, ReplicationRole, ReplicationStatus, StreamChangesRequest, StreamChangesResponse,
68};
69pub use result::{
70    AggregateResult, AggregateValue, ColumnData, Edge, EdgeBlock, EntityBlock, MutationResult,
71    QueryResult,
72};
73pub use value::Value;
74
75/// Protocol version for wire compatibility.
76///
77/// This version is included in handshake messages to ensure client and server
78/// can communicate properly. When the protocol changes in incompatible ways,
79/// this version should be incremented.
80pub const PROTOCOL_VERSION: u32 = 1;
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn test_protocol_version() {
88        assert_eq!(PROTOCOL_VERSION, 1);
89    }
90
91    #[test]
92    fn test_value_roundtrip() {
93        let value = Value::String("hello".into());
94        let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&value).unwrap();
95        let archived =
96            rkyv::access::<value::ArchivedValue, rkyv::rancor::Error>(&bytes).unwrap();
97        let deserialized: Value =
98            rkyv::deserialize::<Value, rkyv::rancor::Error>(archived).unwrap();
99        assert_eq!(value, deserialized);
100    }
101
102    #[test]
103    fn test_request_roundtrip() {
104        let request = Request::query(
105            1,
106            5,
107            GraphQuery::new("User")
108                .with_fields(vec!["id".into(), "name".into()])
109                .include(RelationInclude::new("posts"))
110                .with_filter(FilterExpr::eq("active", true).into())
111                .with_order(OrderSpec::asc("name"))
112                .with_pagination(Pagination::limit(10)),
113        );
114
115        let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&request).unwrap();
116        let archived =
117            rkyv::access::<message::ArchivedRequest, rkyv::rancor::Error>(&bytes).unwrap();
118        let deserialized: Request =
119            rkyv::deserialize::<Request, rkyv::rancor::Error>(archived).unwrap();
120        assert_eq!(request, deserialized);
121    }
122
123    #[test]
124    fn test_response_roundtrip() {
125        let response = Response::query_ok(
126            1,
127            QueryResult::new(
128                vec![EntityBlock::with_data(
129                    "User",
130                    vec![[1u8; 16]],
131                    vec![
132                        ColumnData::new("id", vec![Value::Uuid([1u8; 16])]),
133                        ColumnData::new("name", vec![Value::String("Alice".into())]),
134                    ],
135                )],
136                vec![],
137                false,
138            ),
139        );
140
141        let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&response).unwrap();
142        let archived =
143            rkyv::access::<message::ArchivedResponse, rkyv::rancor::Error>(&bytes).unwrap();
144        let deserialized: Response =
145            rkyv::deserialize::<Response, rkyv::rancor::Error>(archived).unwrap();
146        assert_eq!(response, deserialized);
147    }
148}