use std::{fmt, mem::swap, ops::Deref, slice};
use serde::{Deserialize, Deserializer, Serialize, de::Visitor};
use serde_json::{Number as JsonNumber, Value};
use serde_value::ValueDeserializer;
use serde_with::skip_serializing_none;
use crate::openapi::{Map, MaybeRef, macros};
macros::define_openapi_spec_object! {
[override_with]:
#[derive(Default)]
pub struct Schema {
#[serde(rename = "type")]
pub schema_type: Option<SchemaType>,
#[serde(rename = "enum")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub enum_values: Vec<Value>,
#[serde(rename = "const")]
pub const_value: Option<Value>,
pub format: Option<Format>,
#[serde(skip_serializing_if = "Map::is_empty")]
pub properties: Map<String, MaybeRef<Schema>>,
pub property_names: Option<Box<MaybeRef<Schema>>>,
pub additional_properties: Option<Box<MaybeRef<Schema>>>,
pub max_properties: Option<u64>,
pub min_properties: Option<u64>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub required: Vec<String>,
#[serde(skip_serializing_if = "Map::is_empty")]
pub dependent_required: Map<String, Vec<String>>,
pub multiple_of: Option<JsonNumber>,
pub maximum: Option<JsonNumber>,
pub exclusive_maximum: Option<JsonNumber>,
pub minimum: Option<JsonNumber>,
pub exclusive_minimum: Option<JsonNumber>,
pub max_length: Option<u64>,
pub min_length: Option<u64>,
pub pattern: Option<String>,
pub content_encoding: Option<String>,
pub content_media_type: Option<String>,
pub content_schema: Option<Box<MaybeRef<Schema>>>,
pub items: Option<ArrayItems>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub prefix_items: Vec<MaybeRef<Schema>>,
pub max_items: Option<u64>,
pub min_items: Option<u64>,
pub unique_items: Option<bool>,
pub max_contains: Option<u64>,
pub min_contains: Option<u64>,
pub title: Option<String>,
pub description: Option<String>,
pub default: Option<Value>,
pub deprecated: Option<bool>,
pub read_only: Option<bool>,
pub write_only: Option<bool>,
pub example: Option<Value>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<Value>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub all_of: Vec<MaybeRef<Schema>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub one_of: Vec<MaybeRef<Schema>>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub any_of: Vec<MaybeRef<Schema>>,
pub discriminator: Option<Discriminator>,
}
}
impl Schema {
pub fn new_string_constant(string: impl Into<String>) -> Self {
Schema::default()
.with_schema_type(Type::String)
.with_enum_values(vec![Value::String(string.into())])
}
pub fn null() -> Self {
Schema::default().with_schema_type(Type::Null)
}
}
#[derive(PartialEq, Eq, Clone)]
pub enum ArrayItems {
False,
Schema(Box<MaybeRef<Schema>>),
}
impl<I: Into<MaybeRef<Schema>>> From<I> for ArrayItems {
fn from(value: I) -> Self {
Self::Schema(Box::new(value.into()))
}
}
impl Serialize for ArrayItems {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
Self::False => serializer.serialize_bool(false),
Self::Schema(schema) => schema.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for ArrayItems {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Vis;
impl<'de> Visitor<'de> for Vis {
type Value = ArrayItems;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("false")
}
fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if !v {
Ok(ArrayItems::False)
} else {
Err(E::custom("array items must be false or a schema object"))
}
}
}
let value = deserializer.deserialize_any(serde_value::ValueVisitor)?;
if let Ok(schema) = Box::deserialize(ValueDeserializer::<D::Error>::new(value.clone())) {
return Ok(Self::Schema(schema));
}
ValueDeserializer::<D::Error>::new(value).deserialize_bool(Vis)
}
}
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SchemaType {
Single(Type),
Multiple(Vec<Type>),
}
impl SchemaType {
pub fn push(&mut self, ty: Type) {
match self {
Self::Single(prev_ty) => {
let mut swap_target = Type::Null;
swap(prev_ty, &mut swap_target);
*self = Self::Multiple(vec![swap_target, ty])
}
Self::Multiple(prev_types) => prev_types.push(ty),
}
}
}
impl Deref for SchemaType {
type Target = [Type];
fn deref(&self) -> &Self::Target {
match self {
Self::Single(single) => slice::from_ref(single),
Self::Multiple(multiple) => multiple,
}
}
}
impl From<Type> for SchemaType {
fn from(value: Type) -> Self {
Self::Single(value)
}
}
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub enum Type {
Null,
Boolean,
Object,
Array,
Number,
String,
Integer,
}
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum Format {
Int8,
Int16,
Int32,
Int64,
Uint8,
Uint16,
Uint32,
Uint64,
Float,
Double,
Char,
Binary,
DateTimeLocal,
DateTime,
Date,
Time,
Duration,
Byte,
Base64Url,
Commonmark,
Decimal,
Email,
IdnEmail,
Hostname,
IdnHostname,
IPv4,
IPv6,
Uri,
UriReference,
Iri,
IriReference,
Uuid,
UriTemplate,
JsonPointer,
RelativeJsonPointer,
Regex,
#[serde(untagged)]
Other(String),
}
#[skip_serializing_none]
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Discriminator {
pub property_name: String,
#[serde(skip_serializing_if = "Map::is_empty")]
pub mapping: Map<String, String>,
pub default_mapping: Option<String>,
}
impl Discriminator {
pub fn new(property_name: String) -> Self {
Self {
property_name,
mapping: Default::default(),
default_mapping: Default::default(),
}
}
}