json_api/doc/
error.rs

1use doc::Link;
2use http::StatusCode;
3use value::{Key, Map};
4
5/// Contains information about problems encountered while performing an
6/// operation.
7///
8/// For more information, check out the *[error objects]* section of the JSON API
9/// specification.
10///
11/// [error objects]: http://jsonapi.org/format/#error-objects
12#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
13pub struct ErrorObject {
14    /// An application-specific error code, expressed as a string value.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub code: Option<String>,
17
18    /// A human-readable explanation specific to this occurrence of the problem.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub detail: Option<String>,
21
22    /// A unique identifier for this particular occurrence of the problem.
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub id: Option<String>,
25
26    /// Contains relevant links. If this value of this field is empty, it will not be
27    /// serialized. For more information, check out the *[links]* section of the JSON
28    /// API specification.
29    ///
30    /// [links]: https://goo.gl/E4E6Vt
31    #[serde(default, skip_serializing_if = "Map::is_empty")]
32    pub links: Map<Key, Link>,
33
34    /// Non-standard meta information. If this value of this field is empty, it will not
35    /// be serialized. For more information, check out the *[meta information]* section
36    /// of the JSON API specification.
37    ///
38    /// [meta information]: https://goo.gl/LyrGF8
39    #[serde(default, skip_serializing_if = "Map::is_empty")]
40    pub meta: Map,
41
42    /// The source of the error.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub source: Option<ErrorSource>,
45
46    /// The HTTP status code applicable to this problem.
47    #[serde(skip_serializing_if = "Option::is_none", with = "serde_status")]
48    pub status: Option<StatusCode>,
49
50    /// A short, human-readable summary of the problem.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub title: Option<String>,
53
54    /// Private field for backwards compatibility.
55    #[serde(skip)]
56    _ext: (),
57}
58
59impl ErrorObject {
60    /// Returns a new `ErrorObject` with the specified `status`.
61    pub fn new(status: Option<StatusCode>) -> Self {
62        let title = status
63            .and_then(|value| value.canonical_reason())
64            .map(|reason| reason.to_owned());
65
66        ErrorObject {
67            status,
68            title,
69            ..Default::default()
70        }
71    }
72}
73
74/// References to the source of the error.
75#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
76pub struct ErrorSource {
77    /// A string indicating which query parameter caused the error.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub parameter: Option<String>,
80
81    /// A JSON pointer to the associated entity in the request document.
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub pointer: Option<String>,
84
85    /// Private field for backwards compatibility.
86    #[serde(skip)]
87    _ext: (),
88}
89
90impl ErrorSource {
91    /// Returns a new `ErrorSource` with the specified `parameter` and
92    /// `pointer` values.
93    pub fn new(parameter: Option<String>, pointer: Option<String>) -> Self {
94        ErrorSource {
95            parameter,
96            pointer,
97            _ext: (),
98        }
99    }
100}
101
102mod serde_status {
103    use std::fmt::{self, Formatter};
104
105    use serde::de::{Deserializer, Error, Visitor};
106    use serde::ser::Serializer;
107
108    use http::StatusCode;
109
110    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<StatusCode>, D::Error>
111    where
112        D: Deserializer<'de>,
113    {
114        struct StatusVisitor;
115
116        impl<'de> Visitor<'de> for StatusVisitor {
117            type Value = Option<StatusCode>;
118
119            fn expecting(&self, f: &mut Formatter) -> fmt::Result {
120                f.write_str("a string containing a http status code")
121            }
122
123            fn visit_none<E>(self) -> Result<Self::Value, E> {
124                Ok(None)
125            }
126
127            fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
128            where
129                D: Deserializer<'de>,
130            {
131                deserializer.deserialize_str(self)
132            }
133
134            fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
135                value.parse().map(Some).map_err(Error::custom)
136            }
137        }
138
139        deserializer.deserialize_option(StatusVisitor)
140    }
141
142    pub fn serialize<S>(
143        value: &Option<StatusCode>,
144        serializer: S,
145    ) -> Result<S::Ok, S::Error>
146    where
147        S: Serializer,
148    {
149        match *value {
150            Some(status) => serializer.serialize_str(status.as_str()),
151            None => serializer.serialize_none(),
152        }
153    }
154}