#![warn(missing_docs)]
#![warn(rustdoc::broken_intra_doc_links)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
pub mod openapi;
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
#[doc(hidden)]
pub mod gen;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::option::Option;
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
pub use utoipa_gen::*;
pub trait OpenApi {
fn openapi() -> openapi::OpenApi;
}
pub trait ToSchema: PartialSchema {
fn name() -> Cow<'static, str> {
let full_type_name = std::any::type_name::<Self>();
let type_name_without_generic = full_type_name
.split_once("<")
.map(|(s1, _)| s1)
.unwrap_or(full_type_name);
let type_name = type_name_without_generic
.rsplit_once("::")
.map(|(_, tn)| tn)
.unwrap_or(type_name_without_generic);
Cow::Borrowed(type_name)
}
#[allow(unused)]
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
}
}
impl<T: ToSchema> From<T> for openapi::RefOr<openapi::schema::Schema> {
fn from(_: T) -> Self {
T::schema()
}
}
pub type TupleUnit = ();
impl PartialSchema for TupleUnit {
fn schema() -> openapi::RefOr<openapi::schema::Schema> {
openapi::schema::empty().into()
}
}
impl ToSchema for TupleUnit {
fn name() -> Cow<'static, str> {
Cow::Borrowed("TupleUnit")
}
}
macro_rules! impl_to_schema {
( $( $ty:ident ),* ) => {
$(
impl ToSchema for $ty {
fn name() -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed(stringify!( $ty ))
}
}
)*
};
}
#[rustfmt::skip]
impl_to_schema!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
);
impl ToSchema for &str {
fn name() -> Cow<'static, str> {
str::name()
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for Option<T>
where
Option<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for Vec<T>
where
Vec<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for std::collections::LinkedList<T>
where
std::collections::LinkedList<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for [T]
where
[T]: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<'t, T: ToSchema> ToSchema for &'t [T]
where
&'t [T]: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<'t, T: ToSchema> ToSchema for &'t mut [T]
where
&'t mut [T]: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<K: ToSchema, T: ToSchema, S> ToSchema for std::collections::HashMap<K, T, S>
where
std::collections::HashMap<K, T, S>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<K: ToSchema, T: ToSchema> ToSchema for std::collections::BTreeMap<K, T>
where
std::collections::BTreeMap<K, T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<K: ToSchema, S> ToSchema for std::collections::HashSet<K, S>
where
std::collections::HashSet<K, S>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<K: ToSchema> ToSchema for std::collections::BTreeSet<K>
where
std::collections::BTreeSet<K>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
}
}
#[cfg(all(feature = "macros", feature = "indexmap"))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
impl<K: ToSchema, T: ToSchema> ToSchema for indexmap::IndexMap<K, T>
where
indexmap::IndexMap<K, T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
T::schemas(schemas);
}
}
#[cfg(all(feature = "macros", feature = "indexmap"))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
impl<K: ToSchema> ToSchema for indexmap::IndexSet<K>
where
indexmap::IndexSet<K>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
K::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for std::boxed::Box<T>
where
std::boxed::Box<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<'a, T: ToSchema + Clone> ToSchema for std::borrow::Cow<'a, T>
where
std::borrow::Cow<'a, T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
impl<T: ToSchema> ToSchema for std::cell::RefCell<T>
where
std::cell::RefCell<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(all(feature = "macros", feature = "rc_schema"))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
impl<T: ToSchema> ToSchema for std::rc::Rc<T>
where
std::rc::Rc<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
#[cfg(all(feature = "macros", feature = "rc_schema"))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
impl<T: ToSchema> ToSchema for std::sync::Arc<T>
where
std::sync::Arc<T>: PartialSchema,
{
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
T::schemas(schemas);
}
}
impl PartialSchema for serde_json::Value {
fn schema() -> openapi::RefOr<openapi::schema::Schema> {
utoipa::openapi::schema::Object::builder()
.schema_type(utoipa::openapi::schema::SchemaType::AnyValue)
.into()
}
}
impl ToSchema for serde_json::Value {}
#[doc(hidden)]
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
mod utoipa {
pub use super::*;
}
pub trait PartialSchema {
fn schema() -> openapi::RefOr<openapi::schema::Schema>;
}
pub trait Path {
fn methods() -> Vec<openapi::path::HttpMethod>;
fn path() -> String;
fn operation() -> openapi::path::Operation;
}
pub trait Modify {
fn modify(&self, openapi: &mut openapi::OpenApi);
}
pub trait IntoParams {
fn into_params(
parameter_in_provider: impl Fn() -> Option<openapi::path::ParameterIn>,
) -> Vec<openapi::path::Parameter>;
}
pub trait IntoResponses {
fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>>;
}
#[cfg(feature = "auto_into_responses")]
impl<T: IntoResponses, E: IntoResponses> IntoResponses for Result<T, E> {
fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
let mut responses = T::responses();
responses.append(&mut E::responses());
responses
}
}
#[cfg(feature = "auto_into_responses")]
impl IntoResponses for () {
fn responses() -> BTreeMap<String, openapi::RefOr<openapi::response::Response>> {
BTreeMap::new()
}
}
pub trait ToResponse<'__r> {
fn response() -> (&'__r str, openapi::RefOr<openapi::response::Response>);
}
#[derive(Clone, serde::Deserialize, serde::Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[serde(untagged)]
pub enum Number {
Int(isize),
UInt(usize),
Float(f64),
}
impl Eq for Number {}
impl PartialEq for Number {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Int(left), Self::Int(right)) => left == right,
(Self::UInt(left), Self::UInt(right)) => left == right,
(Self::Float(left), Self::Float(right)) => left == right,
_ => false,
}
}
}
macro_rules! impl_from_for_number {
( $( $ty:ident => $pat:ident $( as $as:ident )? ),* ) => {
$(
impl From<$ty> for Number {
fn from(value: $ty) -> Self {
Self::$pat(value $( as $as )?)
}
}
)*
};
}
#[rustfmt::skip]
impl_from_for_number!(
f32 => Float as f64, f64 => Float,
i8 => Int as isize, i16 => Int as isize, i32 => Int as isize, i64 => Int as isize,
u8 => UInt as usize, u16 => UInt as usize, u32 => UInt as usize, u64 => UInt as usize,
isize => Int, usize => UInt
);
#[doc(hidden)]
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
pub mod __dev {
use utoipa_gen::schema;
use crate::{utoipa, OpenApi, PartialSchema};
pub trait PathConfig {
fn path() -> String;
fn methods() -> Vec<crate::openapi::path::HttpMethod>;
fn tags_and_operation() -> (Vec<&'static str>, utoipa::openapi::path::Operation);
}
pub trait Tags<'t> {
fn tags() -> Vec<&'t str>;
}
impl<T: PathConfig> utoipa::Path for T {
fn path() -> String {
<Self as PathConfig>::path()
}
fn methods() -> Vec<crate::openapi::path::HttpMethod> {
<Self as PathConfig>::methods()
}
fn operation() -> crate::openapi::path::Operation {
let (tags, mut operation) = <Self as PathConfig>::tags_and_operation();
let operation_tags = operation.tags.get_or_insert(Vec::new());
operation_tags.extend(tags.iter().map(ToString::to_string));
operation
}
}
pub trait NestedApiConfig {
fn config() -> (utoipa::openapi::OpenApi, Vec<&'static str>, &'static str);
}
impl<T: NestedApiConfig> OpenApi for T {
fn openapi() -> crate::openapi::OpenApi {
let (mut api, tags, module_path) = T::config();
api.paths.paths.iter_mut().for_each(|(_, path_item)| {
let update_tags = |operation: Option<&mut crate::openapi::path::Operation>| {
if let Some(operation) = operation {
let operation_tags = operation.tags.get_or_insert(Vec::new());
operation_tags.extend(tags.iter().map(ToString::to_string));
if operation_tags.is_empty() && !module_path.is_empty() {
operation_tags.push(module_path.to_string());
}
}
};
update_tags(path_item.get.as_mut());
update_tags(path_item.put.as_mut());
update_tags(path_item.post.as_mut());
update_tags(path_item.delete.as_mut());
update_tags(path_item.options.as_mut());
update_tags(path_item.head.as_mut());
update_tags(path_item.patch.as_mut());
update_tags(path_item.trace.as_mut());
});
api
}
}
pub trait ComposeSchema {
fn compose(
new_generics: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>;
}
macro_rules! impl_compose_schema {
( $( $ty:ident ),* ) => {
$(
impl ComposeSchema for $ty {
fn compose(_: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema!( $ty ).into()
}
}
)*
};
}
#[rustfmt::skip]
impl_compose_schema!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, bool, f32, f64, String, str, char
);
fn schema_or_compose<T: ComposeSchema>(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
index: usize,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
if let Some(schema) = schemas.get(index) {
schema.clone()
} else {
T::compose(schemas)
}
}
impl ComposeSchema for &str {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
str::compose(schemas)
}
}
impl<T: ComposeSchema + ?Sized> PartialSchema for T {
fn schema() -> crate::openapi::RefOr<crate::openapi::schema::Schema> {
T::compose(Vec::new())
}
}
impl<T: ComposeSchema> ComposeSchema for Option<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::OneOfBuilder::new()
.item(
utoipa::openapi::schema::ObjectBuilder::new()
.schema_type(utoipa::openapi::schema::Type::Null),
)
.item(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<T: ComposeSchema> ComposeSchema for Vec<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<T: ComposeSchema> ComposeSchema for std::collections::LinkedList<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<T: ComposeSchema> ComposeSchema for [T] {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<T: ComposeSchema> ComposeSchema for &[T] {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<T: ComposeSchema> ComposeSchema for &mut [T] {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<T>(schemas, 0))
.into()
}
}
impl<K: ComposeSchema, T: ComposeSchema, S> ComposeSchema for std::collections::HashMap<K, T, S> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::ObjectBuilder::new()
.property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
.additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
.into()
}
}
impl<K: ComposeSchema, T: ComposeSchema> ComposeSchema for std::collections::BTreeMap<K, T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::ObjectBuilder::new()
.property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
.additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
.into()
}
}
impl<K: ComposeSchema, S> ComposeSchema for std::collections::HashSet<K, S> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<K>(schemas, 0))
.unique_items(true)
.into()
}
}
impl<K: ComposeSchema> ComposeSchema for std::collections::BTreeSet<K> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<K>(schemas, 0))
.unique_items(true)
.into()
}
}
#[cfg(feature = "indexmap")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
impl<K: ComposeSchema, T: ComposeSchema> ComposeSchema for indexmap::IndexMap<K, T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::ObjectBuilder::new()
.property_names(Some(schema_or_compose::<K>(schemas.clone(), 0)))
.additional_properties(Some(schema_or_compose::<T>(schemas, 1)))
.into()
}
}
#[cfg(feature = "indexmap")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "indexmap")))]
impl<K: ComposeSchema> ComposeSchema for indexmap::IndexSet<K> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::schema::ArrayBuilder::new()
.items(schema_or_compose::<K>(schemas, 0))
.unique_items(true)
.into()
}
}
impl<'a, T: ComposeSchema + Clone> ComposeSchema for std::borrow::Cow<'a, T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema_or_compose::<T>(schemas, 0)
}
}
impl<T: ComposeSchema> ComposeSchema for std::boxed::Box<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema_or_compose::<T>(schemas, 0)
}
}
impl<T: ComposeSchema> ComposeSchema for std::cell::RefCell<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema_or_compose::<T>(schemas, 0)
}
}
#[cfg(feature = "rc_schema")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
impl<T: ComposeSchema> ComposeSchema for std::rc::Rc<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema_or_compose::<T>(schemas, 0)
}
}
#[cfg(feature = "rc_schema")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros", feature = "rc_schema")))]
impl<T: ComposeSchema> ComposeSchema for std::sync::Arc<T> {
fn compose(
schemas: Vec<utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>>,
) -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
schema_or_compose::<T>(schemas, 0)
}
}
pub trait SchemaReferences {
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
);
}
}
#[cfg(test)]
mod tests {
use insta::assert_compact_json_snapshot;
use serde_json::json;
use super::*;
#[test]
fn test_toschema_name() {
struct Foo;
impl ToSchema for Foo {}
impl PartialSchema for Foo {
fn schema() -> openapi::RefOr<openapi::schema::Schema> {
Default::default()
}
}
assert_eq!(Foo::name(), Cow::Borrowed("Foo"));
struct FooGeneric<T: ToSchema, U: ToSchema>(T, U);
impl<T: ToSchema, U: ToSchema> ToSchema for FooGeneric<T, U> {}
impl<T: ToSchema, U: ToSchema> PartialSchema for FooGeneric<T, U> {
fn schema() -> openapi::RefOr<openapi::schema::Schema> {
Default::default()
}
}
assert_eq!(
FooGeneric::<Foo, String>::name(),
Cow::Borrowed("FooGeneric")
);
assert_eq!(
FooGeneric::<Foo, String>::name(),
FooGeneric::<(), ()>::name(),
);
}
#[cfg(not(feature = "non_strict_integers"))]
#[test]
fn test_partial_schema_strict_integers() {
assert_compact_json_snapshot!(i8::schema(), @r#"{"type": "integer", "format": "int32"}"#);
assert_compact_json_snapshot!(i16::schema(), @r#"{"type": "integer", "format": "int32"}"#);
assert_compact_json_snapshot!(i32::schema(), @r#"{"type": "integer", "format": "int32"}"#);
assert_compact_json_snapshot!(i64::schema(), @r#"{"type": "integer", "format": "int64"}"#);
assert_compact_json_snapshot!(i128::schema(), @r#"{"type": "integer"}"#);
assert_compact_json_snapshot!(isize::schema(), @r#"{"type": "integer"}"#);
assert_compact_json_snapshot!(u8::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
assert_compact_json_snapshot!(u16::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
assert_compact_json_snapshot!(u32::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
assert_compact_json_snapshot!(u64::schema(), @r#"{"type": "integer", "format": "int64", "minimum": 0}"#);
}
#[cfg(feature = "non_strict_integers")]
#[test]
fn test_partial_schema_non_strict_integers() {
assert_compact_json_snapshot!(i8::schema(), @r#"{"type": "integer", "format": "int8"}"#);
assert_compact_json_snapshot!(i16::schema(), @r#"{"type": "integer", "format": "int16"}"#);
assert_compact_json_snapshot!(i32::schema(), @r#"{"type": "integer", "format": "int32"}"#);
assert_compact_json_snapshot!(i64::schema(), @r#"{"type": "integer", "format": "int64"}"#);
assert_compact_json_snapshot!(i128::schema(), @r#"{"type": "integer"}"#);
assert_compact_json_snapshot!(isize::schema(), @r#"{"type": "integer"}"#);
assert_compact_json_snapshot!(u8::schema(), @r#"{"type": "integer", "format": "uint8", "minimum": 0}"#);
assert_compact_json_snapshot!(u16::schema(), @r#"{"type": "integer", "format": "uint16", "minimum": 0}"#);
assert_compact_json_snapshot!(u32::schema(), @r#"{"type": "integer", "format": "int32", "minimum": 0}"#);
assert_compact_json_snapshot!(u64::schema(), @r#"{"type": "integer", "format": "int64", "minimum": 0}"#);
}
#[test]
fn test_partial_schema() {
for (name, schema, value) in [
("bool", bool::schema(), json!({"type": "boolean"})),
("str", str::schema(), json!({"type": "string"})),
("String", String::schema(), json!({"type": "string"})),
("char", char::schema(), json!({"type": "string"})),
(
"f32",
f32::schema(),
json!({"type": "number", "format": "float"}),
),
(
"f64",
f64::schema(),
json!({"type": "number", "format": "double"}),
),
] {
println!(
"{name}: {json}",
json = serde_json::to_string(&schema).unwrap()
);
let schema = serde_json::to_value(schema).unwrap();
assert_eq!(schema, value);
}
}
}