use super::models::{
DataType, DataTypeFormat, DefaultOperationRaw, DefaultSchemaRaw, Either, Resolvable,
SecurityScheme,
};
use std::collections::{BTreeMap, BTreeSet};
pub trait Schema: Sized {
fn description(&self) -> Option<&str>;
fn reference(&self) -> Option<&str>;
fn data_type(&self) -> Option<DataType>;
fn format(&self) -> Option<&DataTypeFormat>;
fn items(&self) -> Option<&Resolvable<Self>>;
fn items_mut(&mut self) -> Option<&mut Resolvable<Self>>;
fn additional_properties(&self) -> Option<&Either<bool, Resolvable<Self>>>;
fn additional_properties_mut(&mut self) -> Option<&mut Either<bool, Resolvable<Self>>>;
fn properties(&self) -> Option<&BTreeMap<String, Resolvable<Self>>>;
fn properties_mut(&mut self) -> Option<&mut BTreeMap<String, Resolvable<Self>>>;
fn required_properties(&self) -> Option<&BTreeSet<String>>;
fn enum_variants(&self) -> Option<&[serde_json::Value]>;
fn contains_any(&self) -> bool {
_schema_contains_any(self, vec![])
}
fn set_reference(&mut self, ref_: String);
fn set_cyclic(&mut self, cyclic: bool);
fn is_cyclic(&self) -> bool;
fn name(&self) -> Option<&str>;
fn set_name(&mut self, name: &str);
}
fn _schema_contains_any<'a, S: Schema>(schema: &'a S, mut nodes: Vec<&'a str>) -> bool {
if schema.data_type().is_none() {
return true;
}
if let Some(name) = schema.name() {
if nodes.iter().any(|&n| n == name) {
return false; } else {
nodes.push(name);
}
}
schema
.properties()
.map(|t| {
t.values()
.any(|s| _schema_contains_any(&*s.read().unwrap(), nodes.clone()))
})
.unwrap_or(false)
|| schema
.items()
.map(|s| _schema_contains_any(&*s.read().unwrap(), nodes.clone()))
.unwrap_or(false)
|| schema
.additional_properties()
.map(|e| match e {
Either::Left(extra_props_allowed) => *extra_props_allowed,
Either::Right(s) => _schema_contains_any(&*s.read().unwrap(), nodes),
})
.unwrap_or(false)
}
pub trait TypedData {
fn data_type() -> DataType {
DataType::Object
}
fn format() -> Option<DataTypeFormat> {
None
}
fn max() -> Option<f32> {
None
}
fn min() -> Option<f32> {
None
}
}
macro_rules! impl_type_simple {
($ty:ty) => {
impl TypedData for $ty {}
};
($ty:ty, $dt:expr) => {
impl TypedData for $ty {
fn data_type() -> DataType {
$dt
}
}
};
($ty:ty, $dt:expr, $df:expr) => {
impl TypedData for $ty {
fn data_type() -> DataType {
$dt
}
fn format() -> Option<DataTypeFormat> {
Some($df)
}
}
};
($ty:ty, $dt:expr, $df:expr, $min:expr, $max:expr) => {
impl TypedData for $ty {
fn data_type() -> DataType {
$dt
}
fn format() -> Option<DataTypeFormat> {
Some($df)
}
fn max() -> Option<f32> {
Some($max)
}
fn min() -> Option<f32> {
Some($min)
}
}
};
}
impl<'a> TypedData for &'a str {
fn data_type() -> DataType {
DataType::String
}
}
impl<'a, T: TypedData> TypedData for &'a T {
fn data_type() -> DataType {
T::data_type()
}
fn format() -> Option<DataTypeFormat> {
T::format()
}
}
impl_type_simple!(char, DataType::String);
impl_type_simple!(String, DataType::String);
impl_type_simple!(PathBuf, DataType::String);
impl_type_simple!(bool, DataType::Boolean);
impl_type_simple!(f32, DataType::Number, DataTypeFormat::Float);
impl_type_simple!(f64, DataType::Number, DataTypeFormat::Double);
impl_type_simple!(
i8,
DataType::Integer,
DataTypeFormat::Int32,
i8::MIN as f32,
i8::MAX as f32
);
impl_type_simple!(
i16,
DataType::Integer,
DataTypeFormat::Int32,
i16::MIN as f32,
i16::MAX as f32
);
impl_type_simple!(i32, DataType::Integer, DataTypeFormat::Int32);
impl_type_simple!(
u8,
DataType::Integer,
DataTypeFormat::Int32,
u8::MIN as f32,
u8::MAX as f32
);
impl_type_simple!(
u16,
DataType::Integer,
DataTypeFormat::Int32,
u16::MIN as f32,
u16::MAX as f32
);
impl_type_simple!(u32, DataType::Integer, DataTypeFormat::Int32);
impl_type_simple!(i64, DataType::Integer, DataTypeFormat::Int64);
impl_type_simple!(
i128,
DataType::Integer,
DataTypeFormat::Int64,
i128::MIN as f32,
i128::MAX as f32
);
impl_type_simple!(isize, DataType::Integer, DataTypeFormat::Int64);
impl_type_simple!(u64, DataType::Integer, DataTypeFormat::Int64);
impl_type_simple!(
u128,
DataType::Integer,
DataTypeFormat::Int64,
u128::MIN as f32,
u128::MAX as f32
);
impl_type_simple!(usize, DataType::Integer, DataTypeFormat::Int64);
#[cfg(feature = "actix-multipart")]
impl_type_simple!(
actix_multipart::Multipart,
DataType::File,
DataTypeFormat::Binary
);
#[cfg(feature = "actix-session")]
impl_type_simple!(actix_session::Session);
#[cfg(feature = "actix-identity")]
impl_type_simple!(actix_identity::Identity);
#[cfg(feature = "actix-files")]
impl_type_simple!(
actix_files::NamedFile,
DataType::File,
DataTypeFormat::Binary
);
#[cfg(feature = "jiff01")]
impl_type_simple!(jiff::Timestamp, DataType::String, DataTypeFormat::DateTime);
#[cfg(feature = "jiff01")]
impl_type_simple!(
jiff::Zoned,
DataType::String,
DataTypeFormat::Other );
#[cfg(feature = "jiff01")]
impl_type_simple!(
jiff::civil::DateTime,
DataType::String,
DataTypeFormat::Other );
#[cfg(feature = "jiff01")]
impl_type_simple!(jiff::civil::Date, DataType::String, DataTypeFormat::Date);
#[cfg(feature = "jiff01")]
impl_type_simple!(
jiff::civil::Time,
DataType::String,
DataTypeFormat::Other );
#[cfg(feature = "jiff01")]
impl_type_simple!(
jiff::Span,
DataType::String,
DataTypeFormat::Other );
#[cfg(feature = "chrono")]
impl_type_simple!(
chrono::NaiveDateTime,
DataType::String,
DataTypeFormat::DateTime
);
#[cfg(feature = "chrono")]
impl_type_simple!(chrono::NaiveDate, DataType::String, DataTypeFormat::Date);
#[cfg(feature = "chrono")]
impl_type_simple!(chrono::NaiveTime, DataType::String);
#[cfg(feature = "rust_decimal")]
impl_type_simple!(
rust_decimal::Decimal,
DataType::Number,
DataTypeFormat::Float
);
#[cfg(feature = "url")]
impl_type_simple!(url::Url, DataType::String, DataTypeFormat::Url);
#[cfg(feature = "uuid0")]
impl_type_simple!(uuid0_dep::Uuid, DataType::String, DataTypeFormat::Uuid);
#[cfg(feature = "uuid1")]
impl_type_simple!(uuid1_dep::Uuid, DataType::String, DataTypeFormat::Uuid);
#[cfg(feature = "chrono")]
impl<T: chrono::offset::TimeZone> TypedData for chrono::DateTime<T> {
fn data_type() -> DataType {
DataType::String
}
fn format() -> Option<DataTypeFormat> {
Some(DataTypeFormat::DateTime)
}
}
#[cfg(feature = "chrono")]
#[allow(deprecated)]
impl<T: chrono::offset::TimeZone> TypedData for chrono::Date<T> {
fn data_type() -> DataType {
DataType::String
}
fn format() -> Option<DataTypeFormat> {
Some(DataTypeFormat::Date)
}
}
impl_type_simple!(std::net::IpAddr, DataType::String, DataTypeFormat::Ip);
impl_type_simple!(std::net::Ipv4Addr, DataType::String, DataTypeFormat::IpV4);
impl_type_simple!(std::net::Ipv6Addr, DataType::String, DataTypeFormat::IpV6);
pub trait Apiv2Schema {
fn name() -> Option<String> {
None
}
fn description() -> &'static str {
""
}
fn required() -> bool {
true
}
fn raw_schema() -> DefaultSchemaRaw {
Default::default()
}
fn schema_with_ref() -> DefaultSchemaRaw {
let mut def = Self::raw_schema();
if let Some(n) = Self::name() {
def.reference =
Some(String::from("#/definitions/") + &n.replace('<', "%3C").replace('>', "%3E"));
} else if let Some(n) = def.name.as_ref() {
def.reference =
Some(String::from("#/definitions/") + &n.replace('<', "%3C").replace('>', "%3E"));
}
if !Self::description().is_empty() {
def.description = Some(Self::description().to_owned());
}
def
}
fn security_scheme() -> Option<SecurityScheme> {
None
}
fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
vec![]
}
}
impl Apiv2Schema for () {}
impl Apiv2Schema for serde_json::Value {}
impl Apiv2Schema for serde_yaml::Value {}
impl<T: TypedData> Apiv2Schema for T {
fn raw_schema() -> DefaultSchemaRaw {
DefaultSchemaRaw {
data_type: Some(T::data_type()),
format: T::format(),
maximum: T::max(),
minimum: T::min(),
..Default::default()
}
}
}
#[cfg(feature = "nightly")]
impl<T> Apiv2Schema for Option<T> {
default fn name() -> Option<String> {
None
}
default fn required() -> bool {
false
}
default fn raw_schema() -> DefaultSchemaRaw {
Default::default()
}
default fn security_scheme() -> Option<SecurityScheme> {
None
}
default fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
vec![]
}
}
impl<T: Apiv2Schema> Apiv2Schema for Option<T> {
fn name() -> Option<String> {
T::name()
}
fn required() -> bool {
false
}
fn raw_schema() -> DefaultSchemaRaw {
T::raw_schema()
}
fn security_scheme() -> Option<SecurityScheme> {
T::security_scheme()
}
fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
T::header_parameter_schema()
}
}
#[cfg(feature = "nightly")]
impl<T, E> Apiv2Schema for Result<T, E> {
default fn name() -> Option<String> {
None
}
default fn raw_schema() -> DefaultSchemaRaw {
Default::default()
}
default fn security_scheme() -> Option<SecurityScheme> {
Default::default()
}
default fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
Default::default()
}
}
impl<T: Apiv2Schema, E> Apiv2Schema for Result<T, E> {
fn name() -> Option<String> {
T::name()
}
fn raw_schema() -> DefaultSchemaRaw {
T::raw_schema()
}
fn security_scheme() -> Option<SecurityScheme> {
T::security_scheme()
}
fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
T::header_parameter_schema()
}
}
impl<T: Apiv2Schema + Clone> Apiv2Schema for std::borrow::Cow<'_, T> {
fn name() -> Option<String> {
T::name()
}
fn raw_schema() -> DefaultSchemaRaw {
T::raw_schema()
}
fn security_scheme() -> Option<SecurityScheme> {
T::security_scheme()
}
fn header_parameter_schema() -> Vec<Parameter<DefaultSchemaRaw>> {
T::header_parameter_schema()
}
}
impl<'a, T: Apiv2Schema> Apiv2Schema for &'a [T] {
fn raw_schema() -> DefaultSchemaRaw {
Vec::<T>::raw_schema()
}
}
impl<T: Apiv2Schema, const N: usize> Apiv2Schema for [T; N] {
fn raw_schema() -> DefaultSchemaRaw {
DefaultSchemaRaw {
data_type: Some(DataType::Array),
items: Some(T::schema_with_ref().into()),
..Default::default()
}
}
}
macro_rules! impl_schema_array {
($ty:ty) => {
impl<T: Apiv2Schema> Apiv2Schema for $ty {
fn raw_schema() -> DefaultSchemaRaw {
DefaultSchemaRaw {
data_type: Some(DataType::Array),
items: Some(T::schema_with_ref().into()),
..Default::default()
}
}
}
};
}
macro_rules! impl_schema_map {
($ty:ty) => {
impl<K: ToString, V: Apiv2Schema> Apiv2Schema for $ty {
fn raw_schema() -> DefaultSchemaRaw {
DefaultSchemaRaw {
data_type: Some(DataType::Object),
extra_props: Some(Either::Right(V::schema_with_ref().into())),
..Default::default()
}
}
}
};
}
use crate::v2::models::Parameter;
use std::{collections::*, path::PathBuf};
impl_schema_array!(Vec<T>);
impl_schema_array!(HashSet<T>);
impl_schema_array!(LinkedList<T>);
impl_schema_array!(VecDeque<T>);
impl_schema_array!(BTreeSet<T>);
impl_schema_array!(BinaryHeap<T>);
impl_schema_map!(HashMap<K, V>);
impl_schema_map!(BTreeMap<K, V>);
pub trait Apiv2Operation {
fn operation() -> DefaultOperationRaw;
fn security_definitions() -> BTreeMap<String, SecurityScheme>;
fn definitions() -> BTreeMap<String, DefaultSchemaRaw>;
fn is_visible() -> bool {
true
}
}
pub trait Apiv2Errors {
const ERROR_MAP: &'static [(u16, &'static str)] = &[];
fn update_error_definitions(_op: &mut DefaultOperationRaw) {}
fn update_definitions(_map: &mut BTreeMap<String, DefaultSchemaRaw>) {}
}
impl Apiv2Errors for () {}
#[cfg(feature = "actix-base")]
impl Apiv2Errors for actix_web::Error {}