use std::fmt;
use serde::{Deserialize, Serialize};
use crate::{
Map,
model::{
components::Components,
extensions::Extensions,
external_docs::ExternalDocumentation,
info::Info,
paths::{Paths, item::PathItem},
schema::dialect::OAS_DIALECT,
security::requirement::SecurityRequirement,
server::Server,
tag::Tag,
},
};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum SpecVersion {
#[default]
V3_1,
#[cfg(feature = "openapi32")]
V3_2,
}
impl SpecVersion {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::V3_1 => "3.1.2",
#[cfg(feature = "openapi32")]
Self::V3_2 => "3.2.0",
}
}
#[must_use]
pub fn supports_3_2(self) -> bool {
#[cfg(feature = "openapi32")]
{
self >= Self::V3_2
}
#[cfg(not(feature = "openapi32"))]
{
let _ = self;
false
}
}
}
impl fmt::Display for SpecVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Document {
pub openapi: String,
#[cfg(feature = "openapi32")]
#[serde(rename = "$self", default, skip_serializing_if = "Option::is_none")]
pub self_uri: Option<String>,
pub info: Info,
#[serde(
rename = "jsonSchemaDialect",
default,
skip_serializing_if = "Option::is_none"
)]
pub json_schema_dialect: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub servers: Vec<Server>,
#[serde(default)]
pub paths: Paths,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub webhooks: Map<PathItem>,
#[serde(default, skip_serializing_if = "Components::is_empty")]
pub components: Components,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub security: Vec<SecurityRequirement>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<Tag>,
#[serde(
rename = "externalDocs",
default,
skip_serializing_if = "Option::is_none"
)]
pub external_docs: Option<ExternalDocumentation>,
#[serde(flatten)]
pub extensions: Extensions,
}
impl Document {
#[must_use]
pub fn new(version: SpecVersion, info: Info) -> Self {
Self {
openapi: version.as_str().to_owned(),
info,
..Self::default()
}
}
#[must_use]
pub fn spec_version(&self) -> Option<SpecVersion> {
let mut parts = self.openapi.split('.');
let major = parts.next()?;
let minor = parts.next()?;
match (major, minor) {
("3", "1") => Some(SpecVersion::V3_1),
#[cfg(feature = "openapi32")]
("3", "2") => Some(SpecVersion::V3_2),
_ => None,
}
}
#[must_use]
pub fn effective_dialect(&self) -> &str {
self.json_schema_dialect.as_deref().unwrap_or(OAS_DIALECT)
}
#[must_use]
pub fn with_server(mut self, server: Server) -> Self {
self.servers.push(server);
self
}
#[must_use]
pub fn with_tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
#[must_use]
pub fn with_security(mut self, requirement: SecurityRequirement) -> Self {
self.security.push(requirement);
self
}
}
#[cfg(test)]
mod tests;