pub mod status;
use std::fmt;
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{Error as DeError, MapAccess, Visitor},
ser::SerializeMap,
};
use serde_json::Value;
use crate::{
Map,
model::{
body::media_type::MediaType, extensions::Extensions, link::Link, parameter::header::Header,
reference::RefOr, response::status::StatusPattern,
},
};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Responses {
pub default_response: Option<RefOr<Response>>,
pub responses: Map<RefOr<Response>>,
pub extensions: Extensions,
}
impl Responses {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, status: u16, response: Response) -> Self {
self.responses.insert(
StatusPattern::Code(status).to_string(),
RefOr::Item(response),
);
self
}
#[must_use]
pub fn with_pattern(mut self, pattern: StatusPattern, response: RefOr<Response>) -> Self {
self.responses.insert(pattern.to_string(), response);
self
}
#[must_use]
pub fn with_default(mut self, response: Response) -> Self {
self.default_response = Some(RefOr::Item(response));
self
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.default_response.is_none() && self.responses.is_empty() && self.extensions.is_empty()
}
#[must_use]
pub fn declares_a_response(&self) -> bool {
self.default_response.is_some() || !self.responses.is_empty()
}
#[must_use]
pub fn get(&self, status: u16) -> Option<&RefOr<Response>> {
self.responses.get(&StatusPattern::Code(status).to_string())
}
pub fn merge_from(&mut self, other: &Self) {
if self.default_response.is_none() {
self.default_response.clone_from(&other.default_response);
}
for (key, response) in &other.responses {
if !self.responses.contains_key(key) {
self.responses.insert(key.clone(), response.clone());
}
}
}
}
impl Serialize for Responses {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let len = usize::from(self.default_response.is_some())
+ self.responses.len()
+ self.extensions.0.len();
let mut map = serializer.serialize_map(Some(len))?;
if let Some(default) = &self.default_response {
map.serialize_entry("default", default)?;
}
for (key, response) in &self.responses {
map.serialize_entry(key, response)?;
}
for (key, value) in &self.extensions.0 {
map.serialize_entry(key, value)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for Responses {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct ResponsesVisitor;
impl<'de> Visitor<'de> for ResponsesVisitor {
type Value = Responses;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a map of status patterns to responses")
}
fn visit_map<A: MapAccess<'de>>(self, mut access: A) -> Result<Responses, A::Error> {
let mut responses = Responses::new();
while let Some(key) = access.next_key::<String>()? {
if key == "default" {
responses.default_response = Some(access.next_value()?);
} else if key.starts_with(crate::model::extensions::EXTENSION_PREFIX) {
responses.extensions.0.insert(key, access.next_value()?);
} else {
key.parse::<StatusPattern>().map_err(A::Error::custom)?;
responses.responses.insert(key, access.next_value()?);
}
}
Ok(responses)
}
}
deserializer.deserialize_map(ResponsesVisitor)
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Response {
#[cfg(feature = "openapi32")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub headers: Map<RefOr<Header>>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub content: Map<MediaType>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub links: Map<RefOr<Link>>,
#[serde(flatten)]
pub extensions: Extensions,
}
impl Response {
pub fn new(description: impl Into<String>) -> Self {
Self {
description: Some(description.into()),
..Self::default()
}
}
pub fn with_content(
description: impl Into<String>,
media_type: impl Into<String>,
content: MediaType,
) -> Self {
let mut response = Self::new(description);
response.content.insert(media_type.into(), content);
response
}
#[must_use]
pub fn with_header(mut self, name: impl Into<String>, header: Header) -> Self {
self.headers.insert(name.into(), RefOr::Item(header));
self
}
#[must_use]
pub fn with_link(mut self, name: impl Into<String>, link: Link) -> Self {
self.links.insert(name.into(), RefOr::Item(link));
self
}
#[must_use]
pub fn with_extension(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.extensions.insert(key, value);
self
}
}
#[cfg(test)]
mod tests;