1use std::fmt;
2use std::time::Duration;
3
4use revision::revisioned;
5use serde::{Deserialize, Serialize};
6use surrealdb_types::{
7 Error as TypesError, ErrorDetails, Kind, SerializationError, SurrealValue, Value, kind, object,
8};
9use web_time::Instant;
10
11use crate::expr::TopLevelExpr;
12
13#[revisioned(revision = 1)]
14#[derive(
15 Debug,
16 Copy,
17 Clone,
18 Default,
19 PartialEq,
20 Eq,
21 PartialOrd,
22 Ord,
23 Hash,
24 Serialize,
25 Deserialize,
26 SurrealValue,
27)]
28#[surreal(crate = "surrealdb_types")]
29#[surreal(untagged, lowercase)]
30#[serde(rename_all = "lowercase")]
31pub enum QueryType {
32 #[default]
34 #[surreal(value = none)]
35 Other,
36 Live,
38 Kill,
40}
41
42impl fmt::Display for QueryType {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 QueryType::Other => "other".fmt(f),
46 QueryType::Live => "live".fmt(f),
47 QueryType::Kill => "kill".fmt(f),
48 }
49 }
50}
51
52impl QueryType {
53 pub(crate) fn for_toplevel_expr(expr: &TopLevelExpr) -> Self {
55 match expr {
56 TopLevelExpr::Live(_) => QueryType::Live,
57 TopLevelExpr::Kill(_) => QueryType::Kill,
58 _ => QueryType::Other,
59 }
60 }
61}
62
63#[derive(Debug, Clone)]
65pub struct QueryResult {
66 pub time: Duration,
67 pub result: Result<Value, TypesError>,
68 pub query_type: QueryType,
71}
72
73impl QueryResult {
74 pub fn output(self) -> Result<Value, TypesError> {
76 self.result
77 }
78}
79
80fn into_query_result_value(error: &TypesError) -> Value {
84 let mut details = error.details().clone().into_value();
85
86 if let Value::Object(ref mut obj) = details {
87 obj.insert("result", error.message().to_string());
88 details
89 } else {
90 warn!("ErrorDetails::into_value() did not produce an Object; this is a bug");
91 Value::Object(object! {
92 result: "Failed to serialise error",
93 kind: "Internal",
94 })
95 }
96}
97
98fn from_query_result_value(value: Value) -> Result<TypesError, TypesError> {
101 let Value::Object(mut map) = value else {
102 return Err(TypesError::serialization(
103 "Expected object for query result error".to_string(),
104 SerializationError::Deserialization,
105 ));
106 };
107 let message = map
108 .remove("result")
109 .ok_or_else(|| {
110 TypesError::serialization(
111 "Missing result (message) for query result error".to_string(),
112 SerializationError::Deserialization,
113 )
114 })?
115 .into_string()
116 .map_err(|e| {
117 TypesError::serialization(e.to_string(), SerializationError::Deserialization)
118 })?;
119 let details = ErrorDetails::from_value(Value::Object(map)).unwrap_or(ErrorDetails::Internal);
120 Ok(TypesError::from_details(message, details))
121}
122
123impl SurrealValue for QueryResult {
124 fn kind_of() -> Kind {
125 kind!(
126 {
127 status: "OK",
128 time: string,
129 result: any,
130 query_type: (QueryType::kind_of()),
131 } | {
132 status: "ERR",
133 time: string,
134 result: string,
135 kind: string,
136 details: any,
137 query_type: (QueryType::kind_of()),
138 }
139 )
140 }
141
142 fn is_value(value: &Value) -> bool {
143 value.is_object_and(|map| {
144 map.get("status").is_some_and(Status::is_value)
145 && map.get("time").is_some_and(Value::is_string)
146 && map.get("result").is_some()
147 && map.get("type").is_some_and(QueryType::is_value)
148 })
149 }
150
151 fn into_value(self) -> Value {
152 let mut map = object! {
153 status: Status::from(&self.result).into_value(),
154 time: format!("{:?}", self.time).into_value(),
155 type: self.query_type.into_value(),
156 };
157 match self.result {
158 Ok(v) => {
159 map.insert("result", v);
160 }
161 Err(e) => {
162 let err_val = into_query_result_value(&e);
163 if let Value::Object(err_obj) = err_val {
164 for (k, v) in err_obj.into_inner() {
165 map.insert(k, v);
166 }
167 }
168 }
169 }
170 Value::Object(map)
171 }
172
173 fn from_value(value: Value) -> Result<Self, TypesError> {
174 let Value::Object(mut map) = value else {
176 return Err(TypesError::serialization(
177 "Expected object for QueryResult".to_string(),
178 SerializationError::Deserialization,
179 ));
180 };
181 let Some(status) = map.remove("status") else {
182 return Err(TypesError::serialization(
183 "Expected status for QueryResult".to_string(),
184 SerializationError::Deserialization,
185 ));
186 };
187 let Some(time) = map.remove("time") else {
188 return Err(TypesError::serialization(
189 "Expected time for QueryResult".to_string(),
190 SerializationError::Deserialization,
191 ));
192 };
193 let Some(result) = map.remove("result") else {
194 return Err(TypesError::serialization(
195 "Expected result for QueryResult".to_string(),
196 SerializationError::Deserialization,
197 ));
198 };
199
200 let status = Status::from_value(status)?;
202 let query_type =
203 map.remove("type").map(QueryType::from_value).transpose()?.unwrap_or_default();
204
205 let time = humantime::parse_duration(&time.into_string().map_err(|e| {
206 TypesError::serialization(e.to_string(), SerializationError::Deserialization)
207 })?)
208 .map_err(|e| {
209 TypesError::serialization(e.to_string(), SerializationError::Deserialization)
210 })?;
211
212 let result = match status {
215 Status::Ok => Ok(Value::from_value(result)?),
216 Status::Err => {
217 map.insert("result".to_string(), result);
218 Err(from_query_result_value(Value::Object(map))?)
219 }
220 };
221
222 Ok(QueryResult {
223 time,
224 result,
225 query_type,
226 })
227 }
228}
229
230pub struct QueryResultBuilder {
231 start_time: Instant,
232 result: Result<Value, TypesError>,
233 query_type: QueryType,
234}
235
236impl QueryResultBuilder {
237 pub fn started_now() -> Self {
238 Self {
239 start_time: Instant::now(),
240 result: Ok(Value::None),
241 query_type: QueryType::Other,
242 }
243 }
244
245 pub fn instant_none() -> QueryResult {
246 QueryResult {
247 time: Duration::ZERO,
248 result: Ok(Value::None),
249 query_type: QueryType::Other,
250 }
251 }
252
253 pub fn with_result(mut self, result: Result<Value, TypesError>) -> Self {
254 self.result = result;
255 self
256 }
257
258 pub fn with_query_type(mut self, query_type: QueryType) -> Self {
259 self.query_type = query_type;
260 self
261 }
262
263 pub fn finish(self) -> QueryResult {
264 QueryResult {
265 time: self.start_time.elapsed(),
266 result: self.result,
267 query_type: self.query_type,
268 }
269 }
270
271 pub fn finish_with_result(self, result: Result<Value, TypesError>) -> QueryResult {
272 QueryResult {
273 time: self.start_time.elapsed(),
274 result,
275 query_type: self.query_type,
276 }
277 }
278}
279
280#[revisioned(revision = 1)]
281#[derive(Debug, Serialize, Deserialize, SurrealValue)]
282#[surreal(crate = "surrealdb_types")]
283#[serde(rename_all = "UPPERCASE")]
284#[surreal(untagged, uppercase)]
285pub enum Status {
286 Ok,
287 Err,
288}
289
290impl Status {
291 pub fn is_ok(&self) -> bool {
292 matches!(self, Status::Ok)
293 }
294
295 pub fn is_err(&self) -> bool {
296 matches!(self, Status::Err)
297 }
298}
299
300impl<'a, T, E> From<&'a Result<T, E>> for Status {
301 fn from(result: &'a Result<T, E>) -> Self {
302 match result {
303 Ok(_) => Status::Ok,
304 Err(_) => Status::Err,
305 }
306 }
307}
308
309impl Serialize for QueryResult {
310 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
311 where
312 S: serde::Serializer,
313 {
314 self.clone().into_value().serialize(serializer)
315 }
316}
317
318impl<'de> Deserialize<'de> for QueryResult {
319 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
320 where
321 D: serde::Deserializer<'de>,
322 {
323 let value = Value::deserialize(deserializer)?;
325 QueryResult::from_value(value).map_err(serde::de::Error::custom)
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use surrealdb_types::{AuthError, NotAllowedError, NotFoundError, ValidationError};
332
333 use super::*;
334
335 fn error_query_result(error: TypesError) -> QueryResult {
336 QueryResult {
337 time: Duration::from_millis(42),
338 result: Err(error),
339 query_type: QueryType::Other,
340 }
341 }
342
343 #[test]
348 fn query_result_error_no_kind_duplication() {
349 let err = TypesError::not_allowed("Token expired".into(), AuthError::TokenExpired);
350 let qr = error_query_result(err);
351 let val = qr.into_value();
352 let Value::Object(ref obj) = val else {
353 panic!("Expected object");
354 };
355
356 assert_eq!(obj.get("status"), Some(&Value::String("ERR".to_string())));
357 assert_eq!(obj.get("result"), Some(&Value::String("Token expired".to_string())));
358 assert_eq!(obj.get("kind"), Some(&Value::String("NotAllowed".to_string())));
359
360 let Some(Value::Object(details)) = obj.get("details") else {
362 panic!("Expected details object");
363 };
364 assert_eq!(
365 details.get("kind"),
366 Some(&Value::String("Auth".to_string())),
367 "details.kind should be the inner variant, not a duplicate of the top-level kind"
368 );
369 }
370
371 #[test]
372 fn query_result_error_round_trip_with_details() {
373 let err = TypesError::not_allowed("Token expired".into(), AuthError::TokenExpired);
374 let qr = error_query_result(err);
375 let val = qr.into_value();
376 let parsed = QueryResult::from_value(val).expect("round-trip should succeed");
377
378 let err = parsed.result.unwrap_err();
379 assert!(err.is_not_allowed());
380 assert_eq!(err.message(), "Token expired");
381 assert!(matches!(
382 err.not_allowed_details(),
383 Some(NotAllowedError::Auth(AuthError::TokenExpired))
384 ));
385 }
386
387 #[test]
388 fn query_result_error_round_trip_nested_struct_details() {
389 let err = TypesError::not_found(
390 "Table not found".into(),
391 NotFoundError::Table {
392 name: "users".into(),
393 },
394 );
395 let qr = error_query_result(err);
396 let val = qr.into_value();
397 let parsed = QueryResult::from_value(val).expect("round-trip should succeed");
398
399 let err = parsed.result.unwrap_err();
400 assert!(err.is_not_found());
401 assert!(matches!(
402 err.not_found_details(),
403 Some(NotFoundError::Table { name }) if name == "users"
404 ));
405 }
406
407 #[test]
408 fn query_result_error_round_trip_no_inner_details() {
409 let err = TypesError::internal("Something went wrong".into());
410 let qr = error_query_result(err);
411 let val = qr.into_value();
412
413 let Value::Object(ref obj) = val else {
414 panic!("Expected object");
415 };
416 assert_eq!(obj.get("kind"), Some(&Value::String("Internal".to_string())));
417 assert!(!obj.contains_key("details"), "Internal errors should have no details");
418
419 let parsed = QueryResult::from_value(val).expect("round-trip should succeed");
420 let err = parsed.result.unwrap_err();
421 assert!(err.is_internal());
422 assert_eq!(err.message(), "Something went wrong");
423 }
424
425 #[test]
426 fn query_result_error_round_trip_validation_parse() {
427 let err = TypesError::validation("Parse error".into(), ValidationError::Parse);
428 let qr = error_query_result(err);
429 let val = qr.into_value();
430 let parsed = QueryResult::from_value(val).expect("round-trip should succeed");
431
432 let err = parsed.result.unwrap_err();
433 assert!(err.is_validation());
434 assert_eq!(err.validation_details(), Some(&ValidationError::Parse));
435 }
436
437 #[test]
438 fn query_result_ok_round_trip() {
439 let qr = QueryResult {
440 time: Duration::from_millis(10),
441 result: Ok(Value::String("hello".to_string())),
442 query_type: QueryType::Other,
443 };
444 let val = qr.into_value();
445 let parsed = QueryResult::from_value(val).expect("round-trip should succeed");
446
447 let v = parsed.result.unwrap();
448 assert_eq!(v, Value::String("hello".to_string()));
449 }
450}