/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 | | } |