arangors/
response.rs

1//! types to deserialize responses from arangoDB server via HTTP request, as
2//! well as convenient functions to deserialize `Response`.
3//!
4//! For response with `error` and `code` fields indicating the whether the
5//! request is successful, use `deserialize_response` to abstract over request
6//! status and data of concerns.
7//!
8//! For response storing all information in `result` filed, use
9//! `ArangoResult`.
10use std::ops::Deref;
11
12use log::trace;
13use serde::{
14    de::{self, DeserializeOwned, Deserializer},
15    Deserialize,
16};
17use serde_json::value::Value;
18
19use crate::{ArangoError, ClientError};
20
21/// Deserialize response from arango server
22///
23/// There are different type of json object when requests to arangoDB
24/// server is accepted or not. Here provides an abstraction for
25/// response of success and failure.
26///
27/// When ArangoDB server response error code, then an error would be cast.
28pub(crate) fn deserialize_response<T>(text: &str) -> Result<T, ClientError>
29where
30    T: DeserializeOwned,
31{
32    let response: Response<T> = serde_json::from_str(text)?;
33    Ok(Into::<Result<T, ArangoError>>::into(response)?)
34}
35
36/// An helper enum to divide into successful and failed response
37///
38/// Request to server can failed at application level, like insufficient
39/// permission, database not found and etc. Response from arangoDB can tell
40/// whether the query succeeded and why if it failed.
41///
42/// The function of this enum is almost the same as `Result`, except that it's
43/// used to deserialize from server response. This enum is to facilitate
44/// deserialization and it should be converted to `Result<T, ArangoError>`
45/// eventually.
46#[derive(Debug)]
47pub(crate) enum Response<T> {
48    Ok(T),
49    Err(ArangoError),
50}
51
52impl<T> From<Response<T>> for Result<T, ArangoError> {
53    fn from(resp: Response<T>) -> Self {
54        match resp {
55            Response::Ok(success) => Ok(success),
56            Response::Err(err) => Err(err),
57        }
58    }
59}
60
61impl<'de, T> Deserialize<'de> for Response<T>
62where
63    T: Deserialize<'de>,
64{
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: Deserializer<'de>,
68    {
69        let map = serde_json::Map::deserialize(deserializer)?;
70        trace!("Deserialize normal Response: {:?}", map);
71        let error = map
72            .get("error")
73            .map_or_else(|| Ok(false), Deserialize::deserialize)
74            .map_err(de::Error::custom)?;
75        let rest = Value::Object(map);
76
77        if error {
78            ArangoError::deserialize(rest)
79                .map(Response::Err)
80                .map_err(de::Error::custom)
81        } else {
82            T::deserialize(rest)
83                .map(Response::Ok)
84                .map_err(de::Error::custom)
85        }
86    }
87}
88
89/// Helper struct to deserialize json result that store
90/// information in "result" field
91#[derive(Deserialize, Debug)]
92pub(crate) struct ArangoResult<T> {
93    #[serde(rename = "result")]
94    result: T,
95}
96
97impl<T> ArangoResult<T> {
98    pub fn unwrap(self) -> T {
99        self.result
100    }
101}
102
103impl<T> Deref for ArangoResult<T> {
104    type Target = T;
105    fn deref(&self) -> &Self::Target {
106        &self.result
107    }
108}
109
110#[cfg(test)]
111mod test {
112    use super::*;
113
114    #[derive(Debug, Deserialize)]
115    pub struct CollectionResponse {
116        pub id: String,
117        pub name: String,
118        pub status: u8,
119        pub r#type: u8,
120        #[serde(rename = "isSystem")]
121        pub is_system: bool,
122    }
123
124    #[test]
125    fn response() {
126        let text = "{\"id\":\"9947\",\"name\":\"relation\",\"status\":2,\"type\":3,\"isSystem\": \
127                    false,\"globallyUniqueId\":\"hD260BE2A30F9/9947\"}";
128        let result = serde_json::from_str::<Response<CollectionResponse>>(text);
129        assert_eq!(result.is_ok(), true, "failed: {:?}", result);
130
131        let text = "{\"error\":false,\"code\":412,\"id\":\"9947\",\"name\":\"relation\",\"status\"\
132                    :2,\"type\":3,\"isSystem\": false,\"globallyUniqueId\":\"hD260BE2A30F9/9947\"}";
133        let result = serde_json::from_str::<Response<CollectionResponse>>(text);
134        assert_eq!(result.is_ok(), true, "failed: {:?}", result);
135
136        let text = "{\"error\":true,\"code\":412,\"errorMessage\":\"error\",\"errorNum\":1200}";
137        let result = serde_json::from_str::<Response<CollectionResponse>>(text);
138        assert_eq!(result.is_ok(), true, "failed: {:?}", result);
139        let response = Into::<Result<_, _>>::into(result.unwrap());
140
141        assert_eq!(
142            response.is_err(),
143            true,
144            "response should be error: {:?}",
145            response
146        );
147    }
148}