Coverage Report

Created: 2025-11-28 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/surrealdb-protocol-0.6.0/rust/lib.rs
Line
Count
Source
1
#![doc = include_str!("../README.md")]
2
3
#[cfg(feature = "proto")]
4
mod convert;
5
#[cfg(feature = "proto")]
6
mod methods;
7
8
#[cfg(feature = "proto")]
9
pub use convert::{TryFromValue, TryIntoValue};
10
11
#[cfg(feature = "rpc")]
12
mod rpc_methods;
13
14
#[cfg(feature = "rpc")]
15
pub use rpc_methods::{QueryResponseValueStream, TryFromQueryStream};
16
17
#[cfg(feature = "proto")]
18
pub mod proto {
19
    //! Protobuf generated code.
20
21
    pub use prost_types;
22
23
    mod generated {
24
        #![allow(missing_docs, clippy::allow_attributes)]
25
26
        pub mod surrealdb {
27
            pub mod protocol {
28
                pub mod v1 {
29
                    include!("../gen/rust/proto/surrealdb.protocol.v1.rs");
30
                }
31
32
                #[cfg(feature = "rpc")]
33
                pub mod rpc {
34
                    pub mod v1 {
35
                        include!("../gen/rust/proto/surrealdb.protocol.rpc.v1.rs");
36
                    }
37
                }
38
            }
39
        }
40
    }
41
42
    pub use generated::surrealdb::protocol::*;
43
}
44
45
#[cfg(feature = "flatbuffers")]
46
pub mod fb {
47
    //! Flatbuffers generated code.
48
49
    mod generated {
50
        #![allow(
51
            clippy::allow_attributes,
52
            clippy::extra_unused_lifetimes,
53
            clippy::missing_safety_doc,
54
            clippy::needless_lifetimes,
55
            missing_docs,
56
            unsafe_op_in_unsafe_fn,
57
            unused_imports
58
        )]
59
        include!("../gen/rust/fb/mod.rs");
60
    }
61
62
    pub use generated::surrealdb::protocol::v_1 as v1;
63
}
64
65
#[cfg(feature = "proto")]
66
mod serde_timestamp {
67
    use prost_types::Timestamp;
68
    use serde::Deserializer;
69
    use serde::Serializer;
70
    use serde::{Deserialize, Serialize};
71
72
0
    pub fn serialize<S>(timestamp: &Timestamp, serializer: S) -> Result<S::Ok, S::Error>
73
0
    where
74
0
        S: Serializer,
75
    {
76
        // Convert to RFC3339 string format
77
0
        let seconds = timestamp.seconds;
78
0
        let nanos = timestamp.nanos;
79
80
        // Create a DateTime-like string representation
81
0
        let dt = chrono::DateTime::from_timestamp(seconds, nanos as u32)
82
0
            .ok_or_else(|| serde::ser::Error::custom("Invalid timestamp"))?;
83
84
0
        dt.to_rfc3339().serialize(serializer)
85
0
    }
86
87
0
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Timestamp, D::Error>
88
0
    where
89
0
        D: Deserializer<'de>,
90
    {
91
0
        let s = String::deserialize(deserializer)?;
92
93
        // Parse RFC3339 string back to timestamp
94
0
        let dt = chrono::DateTime::parse_from_rfc3339(&s).map_err(serde::de::Error::custom)?;
95
96
0
        Ok(Timestamp {
97
0
            seconds: dt.timestamp(),
98
0
            nanos: dt.timestamp_subsec_nanos() as i32,
99
0
        })
100
0
    }
101
}
102
103
#[cfg(feature = "proto")]
104
mod serde_duration {
105
    use prost_types::Duration;
106
    use serde::Deserializer;
107
108
    use serde::{Deserialize, Serialize};
109
110
0
    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
111
0
    where
112
0
        S: serde::Serializer,
113
    {
114
0
        let duration = std::time::Duration::from_secs(duration.seconds as u64)
115
0
            + std::time::Duration::from_nanos(duration.nanos as u64);
116
0
        duration.serialize(serializer)
117
0
    }
118
119
0
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
120
0
    where
121
0
        D: Deserializer<'de>,
122
    {
123
0
        let duration = std::time::Duration::deserialize(deserializer)?;
124
125
0
        Ok(Duration {
126
0
            seconds: duration.as_secs() as i64,
127
0
            nanos: duration.subsec_nanos() as i32,
128
0
        })
129
0
    }
130
}
131
132
#[cfg(feature = "rpc")]
133
mod serde_duration_optional {
134
    use prost_types::Duration;
135
    use serde::{Deserializer, Serializer};
136
137
0
    pub fn serialize<S>(duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
138
0
    where
139
0
        S: Serializer,
140
    {
141
0
        match duration {
142
0
            Some(duration) => crate::serde_duration::serialize(duration, serializer),
143
0
            None => serializer.serialize_none(),
144
        }
145
0
    }
146
147
0
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
148
0
    where
149
0
        D: Deserializer<'de>,
150
    {
151
        struct Visitor;
152
153
        impl serde::de::Visitor<'_> for Visitor {
154
            type Value = Option<Duration>;
155
156
0
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
157
0
                formatter.write_str("a duration or null")
158
0
            }
159
        }
160
161
0
        deserializer.deserialize_option(Visitor)
162
0
    }
163
}
164
165
#[cfg(test)]
166
mod tests {
167
    use std::collections::BTreeMap;
168
169
    use super::*;
170
171
    use crate::proto::v1::{Array, Decimal, File, Geometry, Object, Point, RecordId, Uuid, Value};
172
    use crate::proto::v1::{Line, MultiPolygon, Polygon};
173
    use assert_json_diff::assert_json_eq;
174
    use bytes::Bytes;
175
    use prost_types::{Duration, Timestamp};
176
177
    use rstest::rstest;
178
    use serde_json::json;
179
180
    #[rstest]
181
    #[case(Value::default(), "surrealdb.protocol.v1.Value")]
182
    #[case(
183
        proto::rpc::v1::QueryRequest::default(),
184
        "surrealdb.protocol.rpc.v1.QueryRequest"
185
    )]
186
    fn test_type_names<T: prost::Name>(#[case] _proto: T, #[case] expected: &str) {
187
        assert_eq!(T::full_name(), expected);
188
    }
189
190
    #[rstest]
191
    #[case(Value::null(), json!({"Null":{}}))]
192
    #[case(Value::bool(true), json!({"Bool":true}))]
193
    #[case(Value::int64(1), json!({"Int64":1}))]
194
    #[case(Value::uint64(1), json!({"Uint64":1}))]
195
    #[case(Value::float64(1.0), json!({"Float64":1.0}))]
196
    #[case(Value::string("test".to_string()), json!({"String":"test"}))]
197
    #[case(Value::bytes(Bytes::from_static(b"test")), json!({"Bytes":[116,101,115,116]}))]
198
    #[case(Value::decimal(Decimal::new("1".to_string())), json!({"Decimal":{"value":"1"}}))]
199
    #[case(Value::duration(Duration {
200
        seconds: 1,
201
        nanos: 0,
202
    }), json!({
203
        "Duration": {
204
            "secs":1,
205
            "nanos":0
206
        }
207
    }))]
208
    #[case(Value::datetime(Timestamp {
209
        seconds: 1,
210
        nanos: 0,
211
    }), json!({"Datetime":"1970-01-01T00:00:01+00:00"}))]
212
    #[case(Value::uuid(Uuid::new("00000000-0000-0000-0000-000000000000".to_string())), json!({"Uuid":{"value":"00000000-0000-0000-0000-000000000000"}}))]
213
    #[case(Value::array(Array::new(vec![Value::null()])), json!({"Array":{
214
        "values": [{"Null":{}}]
215
    }}))]
216
    #[case(Value::object(Object::new(BTreeMap::from([("test".to_string(), Value::null())]))), json!({"Object":{
217
        "test": {"Null":{}}
218
    }}))]
219
    #[case(Value::geometry(Geometry::point(Point::new(1., 2.))), json!({"Geometry": {
220
        "Point": {
221
            "x":1.0,
222
            "y":2.0
223
        }
224
    }}))]
225
    #[case(Value::geometry(Geometry::line(Line::new(vec![Point::new(1., 2.), Point::new(3., 4.)]))), json!({"Geometry":{"Line":{"points":[{"x":1.0,"y":2.0},{"x":3.0,"y":4.0}]}}}))]
226
    #[case(Value::geometry(Geometry::polygon(Polygon::new(
227
        Line::new(vec![Point::new(1., 2.), Point::new(3., 4.)]),
228
        vec![Line::new(vec![Point::new(5., 6.), Point::new(7., 8.)])]
229
    ))), json!({"Geometry": {
230
        "Polygon": {
231
            "exterior": {
232
                "points": [
233
                    {"x":1.0,"y":2.0},
234
                    {"x":3.0,"y":4.0}
235
                ]
236
            },
237
            "interiors": [{"points":[{"x":5.0,"y":6.0},{"x":7.0,"y":8.0}]}]
238
        }
239
    }}))]
240
    #[case(Value::geometry(Geometry::multi_polygon(MultiPolygon::new(vec![
241
        Polygon::new(
242
            Line::new(vec![Point::new(1., 2.), Point::new(3., 4.)]),
243
            vec![Line::new(vec![Point::new(5., 6.), Point::new(7., 8.)])]
244
        ),
245
        Polygon::new(
246
            Line::new(vec![Point::new(9., 10.), Point::new(11., 12.)]),
247
            vec![Line::new(vec![Point::new(13., 14.), Point::new(15., 16.)])]
248
    )]))), json!({"Geometry": {
249
        "MultiPolygon": {
250
            "polygons": [
251
                {
252
                    "exterior": {
253
                        "points": [
254
                            {"x":1.0,"y":2.0},
255
                            {"x":3.0,"y":4.0}
256
                        ],
257
                    },
258
                    "interiors": [
259
                        {"points":[{"x":5.0,"y":6.0},{"x":7.0,"y":8.0}]}
260
                    ]
261
                },
262
                {
263
                    "exterior": {
264
                        "points": [
265
                            {"x":9.0,"y":10.0},
266
                            {"x":11.0,"y":12.0}
267
                        ],
268
                    },
269
                    "interiors": [
270
                        {"points":[{"x":13.0,"y":14.0},{"x":15.0,"y":16.0}]}
271
                    ]
272
                }
273
            ]
274
        }
275
    }}))]
276
    #[case(Value::record_id(RecordId::new("test".to_string(), None)), json!({"RecordId":{"table":"test","id":null}}))]
277
    #[case(Value::file(File::new("test".to_string(), "test".to_string())), json!({"File":{"bucket":"test","key":"test"}}))]
278
    fn test_serde(#[case] value: Value, #[case] expected: serde_json::Value) {
279
        let serialized = serde_json::to_value(&value).unwrap();
280
        assert_json_eq!(serialized, expected);
281
    }
282
}