use std::collections::{HashMap, HashSet};
use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc};
pub use gotcha_macro::Schematic;
pub use oas;
use oas::Schema;
pub use serde_json;
pub mod responsible;
pub use responsible::Responsible;
#[cfg(feature = "axum")]
pub mod parameter;
#[cfg(feature = "axum")]
pub use parameter::ParameterProvider;
pub struct EnhancedSchema {
pub schema: Schema,
pub required: bool,
}
pub trait Schematic {
fn name() -> &'static str;
fn required() -> bool;
fn nullable() -> Option<bool> {
None
}
fn type_() -> &'static str;
fn doc() -> Option<String> {
None
}
fn format() -> Option<String> {
None
}
fn fields() -> Vec<(&'static str, EnhancedSchema)> {
vec![]
}
fn generate_schema() -> EnhancedSchema {
EnhancedSchema {
schema: Schema {
_type: Some(Self::type_().to_string()),
format: Self::format(),
nullable: Self::nullable(),
description: Self::doc(),
extras: Default::default(),
},
required: Self::required(),
}
}
fn flatten_schema() -> Option<serde_json::Value> {
None
}
}
macro_rules! impl_primitive_type {
($t: ty, $name: expr, $api_type: expr) => {
impl Schematic for $t {
fn name() -> &'static str {
$name
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
$api_type
}
}
};
}
impl_primitive_type! { i8, "i32", "integer"}
impl_primitive_type! { i16, "i16", "integer"}
impl_primitive_type! { i32, "i32", "integer"}
impl_primitive_type! { i64, "i64", "integer"}
impl_primitive_type! { isize, "isize", "integer"}
impl_primitive_type! { u8, "u8", "integer"}
impl_primitive_type! { u16, "u16", "integer"}
impl_primitive_type! { u32, "u32", "integer"}
impl_primitive_type! { u64, "u64", "integer"}
impl_primitive_type! { usize, "usize", "integer"}
impl_primitive_type! { String, "string", "string"}
impl_primitive_type! { bool, "string", "boolean"}
impl_primitive_type! { f32, "string", "number"}
impl_primitive_type! { f64, "string", "number"}
impl Schematic for () {
fn name() -> &'static str {
"void"
}
fn required() -> bool {
false
}
fn type_() -> &'static str {
"void"
}
}
impl Schematic for &str {
fn name() -> &'static str {
"string"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
}
impl Schematic for uuid::Uuid {
fn name() -> &'static str {
"uuid"
}
fn required() -> bool {
true
}
fn format() -> Option<String> {
Some("uuid".to_string())
}
fn type_() -> &'static str {
"string"
}
}
impl Schematic for chrono::NaiveDateTime {
fn name() -> &'static str {
"datetime"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
fn format() -> Option<String> {
Some("date-time".to_string())
}
}
impl Schematic for chrono::NaiveDate {
fn name() -> &'static str {
"date"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
fn format() -> Option<String> {
Some("date".to_string())
}
}
impl Schematic for serde_json::Value {
fn name() -> &'static str {
"object"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"object"
}
fn format() -> Option<String> {
Some("json".to_string())
}
}
impl<T: Schematic> Schematic for Option<T> {
fn name() -> &'static str {
T::name()
}
fn required() -> bool {
false
}
fn nullable() -> Option<bool> {
Some(true)
}
fn type_() -> &'static str {
T::type_()
}
fn doc() -> Option<String> {
T::doc()
}
fn generate_schema() -> EnhancedSchema {
let enhanced_schema = T::generate_schema();
let mut schema = enhanced_schema.schema;
schema.nullable = Some(true);
EnhancedSchema {
schema,
required: Self::required(),
}
}
}
impl<T: Schematic> Schematic for &T {
fn name() -> &'static str {
T::name()
}
fn required() -> bool {
T::required()
}
fn type_() -> &'static str {
T::type_()
}
fn doc() -> Option<String> {
T::doc()
}
fn generate_schema() -> EnhancedSchema {
T::generate_schema()
}
}
impl<T: Schematic> Schematic for Vec<T> {
fn name() -> &'static str {
T::name()
}
fn required() -> bool {
T::required()
}
fn type_() -> &'static str {
"array"
}
fn generate_schema() -> EnhancedSchema {
let mut schema = EnhancedSchema {
schema: Schema {
_type: Some(Self::type_().to_string()),
format: None,
nullable: None,
description: Self::doc(),
extras: Default::default(),
},
required: Self::required(),
};
schema.schema.extras.insert("items".to_string(), T::generate_schema().schema.to_value());
schema
}
}
impl Schematic for BigDecimal {
fn name() -> &'static str {
"string"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
}
impl Schematic for rust_decimal::Decimal {
fn name() -> &'static str {
"decimal"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
fn format() -> Option<String> {
Some("decimal".to_string())
}
}
impl<T: Schematic> Schematic for HashSet<T> {
fn name() -> &'static str {
T::name()
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"array"
}
fn generate_schema() -> EnhancedSchema {
let mut schema = EnhancedSchema {
schema: Schema {
_type: Some(Self::type_().to_string()),
format: None,
nullable: None,
description: Self::doc(),
extras: Default::default(),
},
required: Self::required(),
};
schema.schema.extras.insert("items".to_string(), T::generate_schema().schema.to_value());
schema
}
}
impl<K: ToString, V: Schematic> Schematic for HashMap<K, V> {
fn name() -> &'static str {
V::name()
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"object"
}
fn generate_schema() -> EnhancedSchema {
let mut schema = EnhancedSchema {
schema: Schema {
_type: Some(Self::type_().to_string()),
format: None,
nullable: None,
description: Self::doc(),
extras: Default::default(),
},
required: Self::required(),
};
schema
.schema
.extras
.insert("additionalProperties".to_string(), V::generate_schema().schema.to_value());
schema
}
}
impl Schematic for DateTime<Utc> {
fn name() -> &'static str {
"string"
}
fn required() -> bool {
true
}
fn type_() -> &'static str {
"string"
}
}