openleadr_wire/
problem.rs

1use http::StatusCode;
2use serde::{Deserialize, Serialize};
3use serde_with::skip_serializing_none;
4
5#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
6pub struct ProblemUri(String);
7
8impl Default for ProblemUri {
9    fn default() -> Self {
10        Self("about:blank".to_string())
11    }
12}
13
14/// Reusable error response. From <https://opensource.zalando.com/problem/schema.yaml>.
15#[skip_serializing_none]
16#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
17#[serde(rename_all = "camelCase")]
18pub struct Problem {
19    /// An absolute URI that identifies the problem type.
20    /// When dereferenced, it SHOULD provide human-readable documentation for the problem type
21    /// (e.g., using HTML).
22    #[serde(default)]
23    pub r#type: ProblemUri,
24    /// A short, summary of the problem type.
25    /// Written in english and readable for engineers
26    /// (usually not suited for non-technical stakeholders and not localized);
27    /// example: Service Unavailable.
28    pub title: Option<String>,
29    /// The HTTP status code generated by the origin server for this occurrence of the problem.
30    #[serde(with = "status_code_serialization")]
31    pub status: StatusCode,
32    /// A human-readable explanation specific to this occurrence of the problem.
33    pub detail: Option<String>,
34    /// An absolute URI that identifies the specific occurrence of the problem.
35    /// It may or may not yield further information if dereferenced.
36    pub instance: Option<String>,
37}
38
39mod status_code_serialization {
40    use super::*;
41
42    use serde::{de::Unexpected, Deserializer, Serializer};
43
44    pub fn serialize<S>(code: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
45    where
46        S: Serializer,
47    {
48        serializer.serialize_u16(code.as_u16())
49    }
50
51    pub fn deserialize<'de, D>(deserializer: D) -> Result<StatusCode, D::Error>
52    where
53        D: Deserializer<'de>,
54    {
55        u16::deserialize(deserializer).and_then(|code| {
56            StatusCode::from_u16(code).map_err(|_| {
57                serde::de::Error::invalid_value(
58                    Unexpected::Unsigned(code as u64),
59                    &"Valid http status code",
60                )
61            })
62        })
63    }
64}