use std::marker::PhantomData;
use crate::{openapi::*, util::*};
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize};
use tracing::warn;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, schemars::JsonSchema)]
pub struct PathItem {
#[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
pub reference: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub get: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub put: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub delete: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub head: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub patch: Option<Operation>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trace: Option<Operation>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub servers: Vec<Server>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<ReferenceOr<Parameter>>,
#[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
pub extensions: IndexMap<String, serde_json::Value>,
}
impl PathItem {
pub fn iter(&self) -> impl Iterator<Item = (&str, &'_ Operation)> {
vec![
("get", &self.get),
("put", &self.put),
("post", &self.post),
("delete", &self.delete),
("options", &self.options),
("head", &self.head),
("patch", &self.patch),
("trace", &self.trace),
]
.into_iter()
.filter_map(|(method, maybe_op)| maybe_op.as_ref().map(|op| (method, op)))
}
pub fn merge(mut self, other: Self) -> Self {
self.merge_with(other);
self
}
pub fn merge_with(&mut self, mut other: Self) {
self.servers.append(&mut other.servers);
self.parameters.append(&mut other.parameters);
for (k, ext) in other.extensions {
self.extensions
.entry(k.clone())
.and_modify(|_| warn!("Conflict on merging extension {}", k))
.or_insert(ext);
}
macro_rules! merge {
($id:ident) => {
if let Some($id) = other.$id {
if self.$id.is_some() {
warn!(
"Conflict on merging {}, ignoring duplicate",
stringify!($id)
);
} else {
let _ = self.$id.insert($id);
}
}
};
}
merge!(reference);
merge!(summary);
merge!(description);
merge!(get);
merge!(put);
merge!(post);
merge!(delete);
merge!(options);
merge!(head);
merge!(patch);
merge!(trace);
}
}
impl IntoIterator for PathItem {
type Item = (&'static str, Operation);
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
vec![
("get", self.get),
("put", self.put),
("post", self.post),
("delete", self.delete),
("options", self.options),
("head", self.head),
("patch", self.patch),
("trace", self.trace),
]
.into_iter()
.filter_map(|(method, maybe_op)| maybe_op.map(|op| (method, op)))
.collect::<Vec<_>>()
.into_iter()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, schemars::JsonSchema)]
pub struct Paths {
#[serde(flatten, deserialize_with = "deserialize_paths")]
pub paths: IndexMap<String, ReferenceOr<PathItem>>,
#[serde(flatten, deserialize_with = "crate::util::deserialize_extensions")]
pub extensions: IndexMap<String, serde_json::Value>,
}
impl Paths {
pub fn iter(&self) -> indexmap::map::Iter<'_, String, ReferenceOr<PathItem>> {
self.paths.iter()
}
}
impl IntoIterator for Paths {
type Item = (String, ReferenceOr<PathItem>);
type IntoIter = indexmap::map::IntoIter<String, ReferenceOr<PathItem>>;
fn into_iter(self) -> Self::IntoIter {
self.paths.into_iter()
}
}
fn deserialize_paths<'de, D>(
deserializer: D,
) -> Result<IndexMap<String, ReferenceOr<PathItem>>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_map(PredicateVisitor(
|key: &String| key.starts_with('/'),
PhantomData,
))
}