openapiv3/
responses.rs

1use std::marker::PhantomData;
2
3use crate::*;
4use indexmap::IndexMap;
5use serde::{Deserialize, Deserializer, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
8pub struct Responses {
9    /// The documentation of responses other than the ones declared
10    /// for specific HTTP response codes. Use this field to cover
11    /// undeclared responses. A Reference Object can link to a response
12    /// that the OpenAPI Object's components/responses section defines.
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub default: Option<ReferenceOr<Response>>,
15    /// Any HTTP status code can be used as the property name,
16    /// but only one property per code, to describe the expected
17    /// response for that HTTP status code. A Reference Object
18    /// can link to a response that is defined in the OpenAPI Object's
19    /// components/responses section. This field MUST be enclosed in
20    /// quotation marks (for example, "200") for compatibility between
21    /// JSON and YAML. To define a range of response codes, this field
22    /// MAY contain the uppercase wildcard character X. For example,
23    /// 2XX represents all response codes between [200-299]. The following
24    /// range definitions are allowed: 1XX, 2XX, 3XX, 4XX, and 5XX.
25    /// If a response range is defined using an explicit code, the
26    /// explicit code definition takes precedence over the range
27    /// definition for that code.
28    #[serde(flatten, deserialize_with = "deserialize_responses")]
29    pub responses: IndexMap<StatusCode, ReferenceOr<Response>>,
30    /// Inline extensions to this object.
31    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
32    pub extensions: IndexMap<String, serde_json::Value>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
36pub struct Response {
37    /// REQUIRED. A short description of the response.
38    /// CommonMark syntax MAY be used for rich text representation.
39    pub description: String,
40
41    /// Maps a header name to its definition.
42    /// RFC7230 states header names are case insensitive.
43    /// If a response header is defined with the name "Content-Type",
44    /// it SHALL be ignored.
45    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
46    pub headers: IndexMap<String, ReferenceOr<Header>>,
47
48    /// A map containing descriptions of potential response payloads.
49    /// The key is a media type or media type range and the value
50    /// describes it. For responses that match multiple keys,
51    /// only the most specific key is applicable. e.g. text/plain
52    /// overrides text/*
53    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
54    pub content: IndexMap<String, MediaType>,
55
56    /// A map of operations links that can be followed from the response.
57    /// The key of the map is a short name for the link, following
58    /// the naming constraints of the names for Component Objects.
59    #[serde(default, skip_serializing_if = "IndexMap::is_empty")]
60    pub links: IndexMap<String, ReferenceOr<Link>>,
61
62    /// Inline extensions to this object.
63    #[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
64    pub extensions: IndexMap<String, serde_json::Value>,
65}
66
67fn deserialize_responses<'de, D>(
68    deserializer: D,
69) -> Result<IndexMap<StatusCode, ReferenceOr<Response>>, D::Error>
70where
71    D: Deserializer<'de>,
72{
73    // We rely on the result of StatusCode::deserialize to act as our
74    // predicate; it will succeed only for status code.
75    deserializer.deserialize_map(PredicateVisitor(|_: &StatusCode| true, PhantomData))
76}
77
78#[cfg(test)]
79mod tests {
80    use serde_json::json;
81
82    use crate::{ReferenceOr, Response, Responses, StatusCode};
83
84    #[test]
85    fn test_responses() {
86        let responses = serde_json::from_str::<Responses>(
87            r#"{
88            "404": {
89                "description": "xxx"
90            },
91            "x-foo": "bar",
92            "ignored": "wat"
93         }"#,
94        )
95        .unwrap();
96
97        assert_eq!(
98            responses.responses.get(&StatusCode::Code(404)),
99            Some(&ReferenceOr::Item(Response {
100                description: "xxx".to_string(),
101                ..Default::default()
102            }))
103        );
104        assert_eq!(responses.extensions.get("x-foo"), Some(&json!("bar")));
105    }
106}