#![warn(missing_docs)]
#![warn(rustdoc::broken_intra_doc_links)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
//! Want to have your API documented with OpenAPI? But you don't want to see the
//! trouble with manual yaml or json tweaking? Would like it to be so easy that it would almost
//! be like utopic? Don't worry utoipa is just there to fill this gap. It aims to do if not all then
//! the most of heavy lifting for you enabling you to focus writing the actual API logic instead of
//! documentation. It aims to be *minimal*, *simple* and *fast*. It uses simple proc macros which
//! you can use to annotate your code to have items documented.
//!
//! Utoipa crate provides autogenerated OpenAPI documentation for Rust REST APIs. It treats
//! code first approach as a first class citizen and simplifies API documentation by providing
//! simple macros for generating the documentation from your code.
//!
//! It also contains Rust types of OpenAPI spec allowing you to write the OpenAPI spec only using
//! Rust if auto-generation is not your flavor or does not fit your purpose.
//!
//! Long term goal of the library is to be the place to go when OpenAPI documentation is needed in Rust
//! codebase.
//!
//! Utoipa is framework agnostic and could be used together with any web framework or even without one. While
//! being portable and standalone one of it's key aspects is simple integration with web frameworks.
//!
//! Currently utoipa provides simple integration with actix-web framework but is not limited to the actix-web
//! framework. All functionalities are not restricted to any specific framework.
//!
//! # Choose your flavor and document your API with ice cold IPA
//!
//! |Flavor|Support|
//! |--|--|
//! |[actix-web](https://github.com/actix/actix-web)|Parse path, path parameters and query parameters, recognize request body and response body, [`utoipa-actix-web` bindings](https://docs.rs/utoipa-actix-web). See more at [docs][actix_path]|
//! |[axum](https://github.com/tokio-rs/axum)|Parse path and query parameters, recognize request body and response body, [`utoipa-axum` bindings](https://docs.rs/utoipa-axum). See more at [docs][axum_path]|
//! |[rocket](https://github.com/SergioBenitez/Rocket)| Parse path, path parameters and query parameters, recognize request body and response body. See more at [docs][rocket_path]|
//! |Others*| Plain `utoipa` without extra flavor. This gives you all the basic benefits listed below in **[Features](#features)** section but with little less automation.|
//!
//! > Others* = For example [warp](https://github.com/seanmonstar/warp) but could be anything.
//!
//! Refer to the existing [examples](https://github.com/juhaku/utoipa/tree/master/examples) to find out more.
//!
//! ## Features
//!
//! * OpenAPI 3.1
//! * Pluggable, easy setup and integration with frameworks.
//! * No bloat, enable what you need.
//! * Support for generic types
//!   * **Note!**<br>
//!     Tuples, arrays and slices cannot be used as generic arguments on types. Types implementing `ToSchema` manually should not have generic arguments, as
//!     they are not composeable and will result compile error.
//! * Automatic schema collection from usages recursively.
//!   * Request body from either handler function arguments (if supported by framework) or from `request_body` attribute.
//!   * Response body from response `body` attribute or response `content` attribute.
//! * Various OpenAPI visualization tools supported out of the box.
//! * Rust type aliases via [`utoipa-config`][utoipa_config].
//!
//! # What's up with the word play?
//!
//! The name comes from words `utopic` and `api` where `uto` is the first three letters of _utopic_
//! and the `ipa` is _api_ reversed. Aaand... `ipa` is also awesome type of beer.
//!
//! # Crate Features
//!
//! * **`macros`** Enable `utoipa-gen` macros. **This is enabled by default.**
//! * **`yaml`** Enables **serde_yaml** serialization of OpenAPI objects.
//! * **`actix_extras`** Enhances [actix-web](https://github.com/actix/actix-web/) integration with being able to
//!   parse `path`, `path` and `query` parameters from actix web path attribute macros. See [actix extras support][actix_path] or
//!   [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
//! * **`rocket_extras`** Enhances [rocket](https://github.com/SergioBenitez/Rocket) framework integration with being
//!   able to parse `path`, `path` and `query` parameters from rocket path attribute macros. See [rocket extras support][rocket_path]
//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details
//! * **`axum_extras`** Enhances [axum](https://github.com/tokio-rs/axum) framework integration allowing users to use `IntoParams`
//!   without defining the `parameter_in` attribute. See [axum extras support][axum_path]
//!   or [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more details.
//! * **`debug`** Add extra traits such as debug traits to openapi definitions and elsewhere.
//! * **`chrono`** Add support for [chrono](https://crates.io/crates/chrono) `DateTime`, `Date`, `NaiveDate`, `NaiveTime` and `Duration`
//!   types. By default these types are parsed to `string` types with additional `format` information.
//!   `format: date-time` for `DateTime` and `format: date` for `Date` and `NaiveDate` according
//!   [RFC3339](https://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14) as `ISO-8601`. To
//!   override default `string` representation users have to use `value_type` attribute to override the type.
//!   See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
//! * **`time`** Add support for [time](https://crates.io/crates/time) `OffsetDateTime`, `PrimitiveDateTime`, `Date`, and `Duration` types.
//!   By default these types are parsed as `string`. `OffsetDateTime` and `PrimitiveDateTime` will use `date-time` format. `Date` will use
//!   `date` format and `Duration` will not have any format. To override default `string` representation users have to use `value_type` attribute
//!   to override the type. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
//! * **`decimal`** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
//!   it is interpreted as `String`. If you wish to change the format you need to override the type.
//!   See the `value_type` in [`ToSchema` derive docs][to_schema_derive].
//! * **`decimal_float`** Add support for [rust_decimal](https://crates.io/crates/rust_decimal) `Decimal` type. **By default**
//!   it is interpreted as `Number`. This feature is mutually exclusive with **decimal** and allow to change the default type used in your
//!   documentation for `Decimal` much like `serde_with_float` feature exposed by rust_decimal.
//! * **`uuid`** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with
//!   format `uuid` in OpenAPI spec.
//! * **`ulid`** Add support for [ulid](https://github.com/dylanhart/ulid-rs). `Ulid` type will be presented as `String` with
//!   format `ulid` in OpenAPI spec.
//! * **`url`** Add support for [url](https://github.com/servo/rust-url). `Url` type will be presented as `String` with
//!   format `uri` in OpenAPI spec.
//! * **`smallvec`** Add support for [smallvec](https://crates.io/crates/smallvec). `SmallVec` will be treated as `Vec`.
//! * **`openapi_extensions`** Adds convenience functions for documenting common scenarios, such as JSON request bodies and responses.
//!   See the [`request_body`](https://docs.rs/utoipa/latest/utoipa/openapi/request_body/index.html) and
//!   [`response`](https://docs.rs/utoipa/latest/utoipa/openapi/response/index.html) docs for examples.
//! * **`repr`** Add support for [repr_serde](https://github.com/dtolnay/serde-repr)'s `repr(u*)` and `repr(i*)` attributes to unit type enums for
//!   C-like enum representation. See [docs](https://docs.rs/utoipa/latest/utoipa/derive.ToSchema.html) for more details.
//! * **`preserve_order`** Preserve order of properties when serializing the schema for a component.
//!   When enabled, the properties are listed in order of fields in the corresponding struct definition.
//!   When disabled, the properties are listed in alphabetical order.
//! * **`preserve_path_order`** Preserve order of OpenAPI Paths according to order they have been
//!   introduced to the `#[openapi(paths(...))]` macro attribute. If disabled the paths will be
//!   ordered in alphabetical order. **However** the operations order under the path **will** be always constant according to
//!   [specification](https://spec.openapis.org/oas/latest.html#fixed-fields-6)
//! * **`indexmap`** Add support for [indexmap](https://crates.io/crates/indexmap). When enabled `IndexMap` will be rendered as a map similar to
//!   `BTreeMap` and `HashMap`.
//! * **`non_strict_integers`** Add support for non-standard integer formats `int8`, `int16`, `uint8`, `uint16`, `uint32`, and `uint64`.
//! * **`rc_schema`** Add `ToSchema` support for `Arc<T>` and `Rc<T>` types. **Note!** serde `rc` feature flag must be enabled separately to allow
//!   serialization and deserialization of `Arc<T>` and `Rc<T>` types. See more about [serde feature flags](https://serde.rs/feature-flags.html).
//! * **`config`** Enables [`utoipa-config`](https://docs.rs/utoipa-config/) for the project which allows
//!   defining global configuration options for `utoipa`.
//!
//! ### Default Library Support
//!
//! * Implicit partial support for `serde` attributes. See [`ToSchema` derive][serde] for more details.
//! * Support for [http](https://crates.io/crates/http) `StatusCode` in responses.
//!
//! # Install
//!
//! Add dependency declaration to Cargo.toml.
//! ```toml
//! [dependencies]
//! utoipa = "5"
//! ```
//!
//! # Examples
//!
//! _**Create type with `ToSchema` and use it in `#[utoipa::path(...)]` that is registered to the `OpenApi`.**_
//!
//! ```rust
//! use utoipa::{OpenApi, ToSchema};
//!
//! #[derive(ToSchema)]
//! struct Pet {
//!    id: u64,
//!    name: String,
//!    age: Option<i32>,
//! }
//! # #[derive(Debug)]
//! # struct NotFound;
//! #
//! # impl std::error::Error for NotFound {}
//! #
//! # impl std::fmt::Display for NotFound {
//! #    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! #        f.write_str("NotFound")
//! #    }
//! # }
//!
//! /// Get pet by id
//! ///
//! /// Get pet from database by pet id
//! #[utoipa::path(
//!     get,
//!     path = "/pets/{id}",
//!     responses(
//!         (status = 200, description = "Pet found successfully", body = Pet),
//!         (status = NOT_FOUND, description = "Pet was not found")
//!     ),
//!     params(
//!         ("id" = u64, Path, description = "Pet database id to get Pet for"),
//!     )
//! )]
//! async fn get_pet_by_id(pet_id: u64) -> Result<Pet, NotFound> {
//!     Ok(Pet {
//!         id: pet_id,
//!         age: None,
//!         name: "lightning".to_string(),
//!     })
//! }
//!
//! #[derive(OpenApi)]
//! #[openapi(paths(get_pet_by_id))]
//! struct ApiDoc;
//!
//! println!("{}", ApiDoc::openapi().to_pretty_json().unwrap());
//! ```
//!
//! # Modify OpenAPI at runtime
//!
//! You can modify generated OpenAPI at runtime either via generated types directly or using
//! [`Modify`] trait.
//!
//! _**Modify generated OpenAPI via types directly.**_
//! ```rust
//! # use utoipa::OpenApi;
//! #[derive(OpenApi)]
//! #[openapi(
//!     info(description = "My Api description"),
//! )]
//! struct ApiDoc;
//!
//! let mut doc = ApiDoc::openapi();
//! doc.info.title = String::from("My Api");
//! ```
//!
//! _**You can even convert the generated [`OpenApi`] to [`openapi::OpenApiBuilder`].**_
//! ```rust
//! # use utoipa::openapi::OpenApiBuilder;
//! # use utoipa::OpenApi;
//! #[derive(OpenApi)]
//! #[openapi(
//!     info(description = "My Api description"),
//! )]
//! struct ApiDoc;
//!
//! let builder: OpenApiBuilder = ApiDoc::openapi().into();
//! ```
//!
//! See [`Modify`] trait for examples on how to modify generated OpenAPI via it.
//!
//! # Go beyond the surface
//!
//! * See how to serve OpenAPI doc via Swagger UI check [`utoipa-swagger-ui`][utoipa_swagger] crate for more details.
//! * Browse to [examples](https://github.com/juhaku/utoipa/tree/master/examples) for more comprehensive examples.
//! * Check [`derive@IntoResponses`] and [`derive@ToResponse`] for examples on deriving responses.
//! * More about OpenAPI security in [security documentation][security].
//! * Dump generated API doc to file at build time. See [issue 214 comment](https://github.com/juhaku/utoipa/issues/214#issuecomment-1179589373).
//!
//! [path]: attr.path.html
//! [rocket_path]: attr.path.html#rocket_extras-feature-support-for-rocket
//! [actix_path]: attr.path.html#actix_extras-feature-support-for-actix-web
//! [axum_path]: attr.path.html#axum_extras-feature-support-for-axum
//! [serde]: derive.ToSchema.html#partial-serde-attributes-support
//! [utoipa_swagger]: https://docs.rs/utoipa-swagger-ui/
//! [utoipa_config]: https://docs.rs/utoipa-config/
//!
//! [security]: openapi/security/index.html
//! [to_schema_derive]: derive.ToSchema.html

pub mod openapi;

#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
#[doc(hidden)]
/// Public re-exports for utoipa-gen.
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::*;

/// Trait for implementing OpenAPI specification in Rust.
///
/// This trait is derivable and can be used with `#[derive]` attribute. The derived implementation
/// will use Cargo provided environment variables to implement the default information. For a details of
/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
///
/// # Examples
///
/// Below is derived example of `OpenApi`.
/// ```rust
/// use utoipa::OpenApi;
/// #[derive(OpenApi)]
/// #[openapi()]
/// struct OpenApiDoc;
/// ```
///
/// This manual `OpenApi` trait implementation is approximately equal to the above derived one except the derive
/// implementation will by default use the Cargo environment variables to set defaults for *application name,
/// version, application description, license, author name & email*.
///
/// ```rust
/// struct OpenApiDoc;
///
/// impl utoipa::OpenApi for OpenApiDoc {
///     fn openapi() -> utoipa::openapi::OpenApi {
///         use utoipa::{ToSchema, Path};
///         utoipa::openapi::OpenApiBuilder::new()
///             .info(utoipa::openapi::InfoBuilder::new()
///                 .title("application name")
///                 .version("version")
///                 .description(Some("application description"))
///                 .license(Some(utoipa::openapi::License::new("MIT")))
///                 .contact(
///                     Some(utoipa::openapi::ContactBuilder::new()
///                         .name(Some("author name"))
///                         .email(Some("author email")).build()),
///             ).build())
///             .paths(utoipa::openapi::path::Paths::new())
///             .components(Some(utoipa::openapi::Components::new()))
///             .build()
///     }
/// }
/// ```
/// [derive]: derive.OpenApi.html
pub trait OpenApi {
    /// Return the [`openapi::OpenApi`] instance which can be parsed with serde or served via
    /// OpenAPI visualization tool such as Swagger UI.
    fn openapi() -> openapi::OpenApi;
}

/// Trait for implementing OpenAPI Schema object.
///
/// Generated schemas can be referenced or reused in path operations.
///
/// This trait is derivable and can be used with `[#derive]` attribute. For a details of
/// `#[derive(ToSchema)]` refer to [derive documentation][derive].
///
/// [derive]: derive.ToSchema.html
///
/// # Examples
///
/// Use `#[derive]` to implement `ToSchema` trait.
/// ```rust
/// # use utoipa::ToSchema;
/// #[derive(ToSchema)]
/// #[schema(example = json!({"name": "bob the cat", "id": 1}))]
/// struct Pet {
///     id: u64,
///     name: String,
///     age: Option<i32>,
/// }
/// ```
///
/// Following manual implementation is equal to above derive one.
/// ```rust
/// # struct Pet {
/// #     id: u64,
/// #     name: String,
/// #     age: Option<i32>,
/// # }
/// #
/// impl utoipa::ToSchema for Pet {
///     fn name() -> std::borrow::Cow<'static, str> {
///         std::borrow::Cow::Borrowed("Pet")
///     }
/// }
/// impl utoipa::PartialSchema for Pet {
///     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
///         utoipa::openapi::ObjectBuilder::new()
///             .property(
///                 "id",
///                 utoipa::openapi::ObjectBuilder::new()
///                     .schema_type(utoipa::openapi::schema::Type::Integer)
///                     .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
///                         utoipa::openapi::KnownFormat::Int64,
///                     ))),
///             )
///             .required("id")
///             .property(
///                 "name",
///                 utoipa::openapi::ObjectBuilder::new()
///                     .schema_type(utoipa::openapi::schema::Type::String),
///             )
///             .required("name")
///             .property(
///                 "age",
///                 utoipa::openapi::ObjectBuilder::new()
///                     .schema_type(utoipa::openapi::schema::Type::Integer)
///                     .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(
///                         utoipa::openapi::KnownFormat::Int32,
///                     ))),
///             )
///             .example(Some(serde_json::json!({
///               "name":"bob the cat","id":1
///             })))
///             .into()
///     }
/// }
/// ```
pub trait ToSchema: PartialSchema {
    /// Return name of the schema.
    ///
    /// Name is used by referencing objects to point to this schema object returned with
    /// [`PartialSchema::schema`] within the OpenAPI document.
    ///
    /// In case a generic schema the _`name`_ will be used as prefix for the name in the OpenAPI
    /// documentation.
    ///
    /// The default implementation naively takes the TypeName by removing
    /// the module path and generic elements.
    /// But you probably don't want to use the default implementation for generic elements.
    /// That will produce collision between generics. (eq. `Foo<String>` )
    ///
    /// # Example
    ///
    /// ```rust
    /// # use utoipa::ToSchema;
    /// #
    /// struct Foo<T>(T);
    ///
    /// impl<T: ToSchema> ToSchema for Foo<T> {}
    /// # impl<T: ToSchema> utoipa::PartialSchema for Foo<T> {
    /// #     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
    /// #         Default::default()
    /// #     }
    /// # }
    ///
    /// assert_eq!(Foo::<()>::name(), std::borrow::Cow::Borrowed("Foo"));
    /// assert_eq!(Foo::<()>::name(), Foo::<i32>::name()); // WARNING: these types have the same name
    /// ```
    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)
    }

    /// Implement reference [`utoipa::openapi::schema::Schema`]s for this type.
    ///
    /// When [`ToSchema`] is being derived this is implemented automatically but if one needs to
    /// manually implement [`ToSchema`] trait then this is needed for `utoipa` to know
    /// referencing schemas that need to be present in the resulting OpenAPI spec.
    ///
    /// The implementation should push to `schemas` [`Vec`] all such field and variant types that
    /// implement `ToSchema` and then call `<MyType as ToSchema>::schemas(schemas)` on that type
    /// to forward the recursive reference collection call on that type.
    ///
    /// # Examples
    ///
    /// _**Implement `ToSchema` manually with references.**_
    ///
    /// ```rust
    /// # use utoipa::{ToSchema, PartialSchema};
    /// #
    /// #[derive(ToSchema)]
    /// struct Owner {
    ///     name: String
    /// }
    ///
    /// struct Pet {
    ///     owner: Owner,
    ///     name: String
    /// }
    /// impl PartialSchema for Pet {
    ///     fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
    ///         utoipa::openapi::schema::Object::builder()
    ///             .property("owner", Owner::schema())
    ///             .property("name", String::schema())
    ///             .into()
    ///     }
    /// }
    /// impl ToSchema for Pet {
    ///     fn schemas(schemas:
    ///         &mut Vec<(String, utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>)>) {
    ///         schemas.push((Owner::name().into(), Owner::schema()));
    ///         <Owner as ToSchema>::schemas(schemas);
    ///     }
    /// }
    /// ```
    #[allow(unused)]
    fn schemas(
        schemas: &mut Vec<(
            String,
            utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
        )>,
    ) {
        // nothing by default
    }
}

impl<T: ToSchema> From<T> for openapi::RefOr<openapi::schema::Schema> {
    fn from(_: T) -> Self {
        T::schema()
    }
}

/// Represents _`nullable`_ type. This can be used anywhere where "nothing" needs to be evaluated.
/// This will serialize to _`null`_ in JSON and [`openapi::schema::empty`] is used to create the
/// [`openapi::schema::Schema`] for the type.
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> ToSchema for std::collections::HashMap<K, T>
where
    std::collections::HashMap<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, 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> ToSchema for std::collections::HashSet<K>
where
    std::collections::HashSet<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<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 {}

// Create `utoipa` module so we can use `utoipa-gen` directly from `utoipa` crate.
// ONLY for internal use!
#[doc(hidden)]
#[cfg(feature = "macros")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "macros")))]
mod utoipa {
    pub use super::*;
}

/// Trait used to implement only _`Schema`_ part of the OpenAPI doc.
///
/// This trait is by default implemented for Rust [`primitive`][primitive] types and some well known types like
/// [`Vec`], [`Option`], [`std::collections::HashMap`] and [`BTreeMap`]. The default implementation adds `schema()`
/// method to the implementing type allowing simple conversion of the type to the OpenAPI Schema
/// object. Moreover this allows handy way of constructing schema objects manually if ever so
/// wished.
///
/// The trait can be implemented manually easily on any type. This trait comes especially handy
/// with [`macro@schema`] macro that can be used to generate schema for arbitrary types.
/// ```rust
/// # use utoipa::PartialSchema;
/// # use utoipa::openapi::schema::{SchemaType, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
/// # use utoipa::openapi::RefOr;
/// #
/// struct MyType;
///
/// impl PartialSchema for MyType {
///     fn schema() -> RefOr<Schema> {
///         // ... impl schema generation here
///         RefOr::T(Schema::Object(ObjectBuilder::new().build()))
///     }
/// }
/// ```
///
/// # Examples
///
/// _**Create number schema from u64.**_
/// ```rust
/// # use utoipa::PartialSchema;
/// # use utoipa::openapi::schema::{Type, KnownFormat, SchemaFormat, ObjectBuilder, Schema};
/// # use utoipa::openapi::RefOr;
/// #
/// let number: RefOr<Schema> = i64::schema().into();
///
// // would be equal to manual implementation
/// let number2 = RefOr::T(
///     Schema::Object(
///         ObjectBuilder::new()
///             .schema_type(Type::Integer)
///             .format(Some(SchemaFormat::KnownFormat(KnownFormat::Int64)))
///             .build()
///         )
///     );
/// # assert_eq!(serde_json::to_value(&number).unwrap(), serde_json::to_value(&number2).unwrap());
/// ```
///
/// _**Construct a Pet object schema manually.**_
/// ```rust
/// # use utoipa::PartialSchema;
/// # use utoipa::openapi::schema::ObjectBuilder;
/// struct Pet {
///     id: i32,
///     name: String,
/// }
///
/// let pet_schema = ObjectBuilder::new()
///     .property("id", i32::schema())
///     .property("name", String::schema())
///     .required("id").required("name")
///     .build();
/// ```
///
/// [primitive]: https://doc.rust-lang.org/std/primitive/index.html
pub trait PartialSchema {
    /// Return ref or schema of implementing type that can then be used to
    /// construct combined schemas.
    fn schema() -> openapi::RefOr<openapi::schema::Schema>;
}

/// Trait for implementing OpenAPI PathItem object with path.
///
/// This trait is implemented via [`#[utoipa::path(...)]`][derive] attribute macro and there
/// is no need to implement this trait manually.
///
/// # Examples
///
/// Use `#[utoipa::path(..)]` to implement Path trait
/// ```rust
/// # #[derive(utoipa::ToSchema)]
/// # struct Pet {
/// #   id: u64,
/// #   name: String,
/// # }
/// #
/// #
/// /// Get pet by id
/// ///
/// /// Get pet from database by pet database id
/// #[utoipa::path(
///     get,
///     path = "/pets/{id}",
///     responses(
///         (status = 200, description = "Pet found successfully", body = Pet),
///         (status = 404, description = "Pet was not found")
///     ),
///     params(
///         ("id" = u64, Path, description = "Pet database id to get Pet for"),
///     )
/// )]
/// async fn get_pet_by_id(pet_id: u64) -> Pet {
///     Pet {
///         id: pet_id,
///         name: "lightning".to_string(),
///     }
/// }
/// ```
///
/// Example of what would manual implementation roughly look like of above `#[utoipa::path(...)]` macro.
/// ```rust
/// utoipa::openapi::PathsBuilder::new().path(
///         "/pets/{id}",
///         utoipa::openapi::PathItem::new(
///             utoipa::openapi::HttpMethod::Get,
///             utoipa::openapi::path::OperationBuilder::new()
///                 .responses(
///                     utoipa::openapi::ResponsesBuilder::new()
///                         .response(
///                             "200",
///                             utoipa::openapi::ResponseBuilder::new()
///                                 .description("Pet found successfully")
///                                 .content("application/json",
///                                     utoipa::openapi::Content::new(
///                                         Some(utoipa::openapi::Ref::from_schema_name("Pet")),
///                                     ),
///                             ),
///                         )
///                         .response("404", utoipa::openapi::Response::new("Pet was not found")),
///                 )
///                 .operation_id(Some("get_pet_by_id"))
///                 .deprecated(Some(utoipa::openapi::Deprecated::False))
///                 .summary(Some("Get pet by id"))
///                 .description(Some("Get pet by id\n\nGet pet from database by pet database id\n"))
///                 .parameter(
///                     utoipa::openapi::path::ParameterBuilder::new()
///                         .name("id")
///                         .parameter_in(utoipa::openapi::path::ParameterIn::Path)
///                         .required(utoipa::openapi::Required::True)
///                         .deprecated(Some(utoipa::openapi::Deprecated::False))
///                         .description(Some("Pet database id to get Pet for"))
///                         .schema(
///                             Some(utoipa::openapi::ObjectBuilder::new()
///                                 .schema_type(utoipa::openapi::schema::Type::Integer)
///                                 .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64)))),
///                         ),
///                 )
///                 .tag("pet_api"),
///         ),
///     );
/// ```
///
/// [derive]: attr.path.html
pub trait Path {
    /// List of HTTP methods this path operation is served at.
    fn methods() -> Vec<openapi::path::HttpMethod>;

    /// The path this operation is served at.
    fn path() -> String;

    /// [`openapi::path::Operation`] describing http operation details such as request bodies,
    /// parameters and responses.
    fn operation() -> openapi::path::Operation;
}

/// Trait that allows OpenApi modification at runtime.
///
/// Implement this trait if you wish to modify the OpenApi at runtime before it is being consumed
/// *(Before `utoipa::OpenApi::openapi()` function returns)*.
/// This is trait can be used to add or change already generated OpenApi spec to alter the generated
/// specification by user defined condition. For example you can add definitions that should be loaded
/// from some configuration at runtime what may not be available during compile time.
///
/// See more about [`OpenApi`][derive] derive at [derive documentation][derive].
///
/// [derive]: derive.OpenApi.html
/// [security_scheme]: openapi/security/enum.SecurityScheme.html
///
/// # Examples
///
/// Add custom JWT [`SecurityScheme`][security_scheme] to [`OpenApi`][`openapi::OpenApi`].
/// ```rust
/// # use utoipa::{OpenApi, Modify};
/// # use utoipa::openapi::security::{SecurityScheme, HttpBuilder, HttpAuthScheme};
/// #[derive(OpenApi)]
/// #[openapi(modifiers(&SecurityAddon))]
/// struct ApiDoc;
///
/// struct SecurityAddon;
///
/// impl Modify for SecurityAddon {
///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
///          openapi.components = Some(
///              utoipa::openapi::ComponentsBuilder::new()
///                  .security_scheme(
///                      "api_jwt_token",
///                      SecurityScheme::Http(
///                          HttpBuilder::new()
///                              .scheme(HttpAuthScheme::Bearer)
///                              .bearer_format("JWT")
///                              .build(),
///                      ),
///                  )
///                  .build(),
///          )
///      }
/// }
/// ```
///
/// Add [OpenAPI Server Object][server] to alter the target server url. This can be used to give context
/// path for api operations.
/// ```rust
/// # use utoipa::{OpenApi, Modify};
/// # use utoipa::openapi::Server;
/// #[derive(OpenApi)]
/// #[openapi(modifiers(&ServerAddon))]
/// struct ApiDoc;
///
/// struct ServerAddon;
///
/// impl Modify for ServerAddon {
///     fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
///         openapi.servers = Some(vec![Server::new("/api")])
///     }
/// }
/// ```
///
/// [server]: https://spec.openapis.org/oas/latest.html#server-object
pub trait Modify {
    /// Apply mutation for [`openapi::OpenApi`] instance before it is returned by
    /// [`openapi::OpenApi::openapi`] method call.
    ///
    /// This function allows users to run arbitrary code to change the generated
    /// [`utoipa::OpenApi`] before it is served.
    fn modify(&self, openapi: &mut openapi::OpenApi);
}

/// Trait used to convert implementing type to OpenAPI parameters.
///
/// This trait is [derivable][derive] for structs which are used to describe `path` or `query` parameters.
/// For more details of `#[derive(IntoParams)]` refer to [derive documentation][derive].
///
/// # Examples
///
/// Derive [`IntoParams`] implementation. This example will fail to compile because [`IntoParams`] cannot
/// be used alone and it need to be used together with endpoint using the params as well. See
/// [derive documentation][derive] for more details.
/// ```
/// use utoipa::{IntoParams};
///
/// #[derive(IntoParams)]
/// struct PetParams {
///     /// Id of pet
///     id: i64,
///     /// Name of pet
///     name: String,
/// }
/// ```
///
/// Roughly equal manual implementation of [`IntoParams`] trait.
/// ```rust
/// # struct PetParams {
/// #    /// Id of pet
/// #    id: i64,
/// #    /// Name of pet
/// #    name: String,
/// # }
/// impl utoipa::IntoParams for PetParams {
///     fn into_params(
///         parameter_in_provider: impl Fn() -> Option<utoipa::openapi::path::ParameterIn>
///     ) -> Vec<utoipa::openapi::path::Parameter> {
///         vec![
///             utoipa::openapi::path::ParameterBuilder::new()
///                 .name("id")
///                 .required(utoipa::openapi::Required::True)
///                 .parameter_in(parameter_in_provider().unwrap_or_default())
///                 .description(Some("Id of pet"))
///                 .schema(Some(
///                     utoipa::openapi::ObjectBuilder::new()
///                         .schema_type(utoipa::openapi::schema::Type::Integer)
///                         .format(Some(utoipa::openapi::SchemaFormat::KnownFormat(utoipa::openapi::KnownFormat::Int64))),
///                 ))
///                 .build(),
///             utoipa::openapi::path::ParameterBuilder::new()
///                 .name("name")
///                 .required(utoipa::openapi::Required::True)
///                 .parameter_in(parameter_in_provider().unwrap_or_default())
///                 .description(Some("Name of pet"))
///                 .schema(Some(
///                     utoipa::openapi::ObjectBuilder::new()
///                         .schema_type(utoipa::openapi::schema::Type::String),
///                 ))
///                 .build(),
///         ]
///     }
/// }
/// ```
/// [derive]: derive.IntoParams.html
pub trait IntoParams {
    /// Provide [`Vec`] of [`openapi::path::Parameter`]s to caller. The result is used in `utoipa-gen` library to
    /// provide OpenAPI parameter information for the endpoint using the parameters.
    fn into_params(
        parameter_in_provider: impl Fn() -> Option<openapi::path::ParameterIn>,
    ) -> Vec<openapi::path::Parameter>;
}

/// This trait is implemented to document a type (like an enum) which can represent multiple
/// responses, to be used in operation.
///
/// # Examples
///
/// ```
/// use std::collections::BTreeMap;
/// use utoipa::{
///     openapi::{Response, ResponseBuilder, ResponsesBuilder, RefOr},
///     IntoResponses,
/// };
///
/// enum MyResponse {
///     Ok,
///     NotFound,
/// }
///
/// impl IntoResponses for MyResponse {
///     fn responses() -> BTreeMap<String, RefOr<Response>> {
///         ResponsesBuilder::new()
///             .response("200", ResponseBuilder::new().description("Ok"))
///             .response("404", ResponseBuilder::new().description("Not Found"))
///             .build()
///             .into()
///     }
/// }
/// ```
pub trait IntoResponses {
    /// Returns an ordered map of response codes to responses.
    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()
    }
}

/// This trait is implemented to document a type which represents a single response which can be
/// referenced or reused as a component in multiple operations.
///
/// _`ToResponse`_ trait can also be derived with [`#[derive(ToResponse)]`][derive].
///
/// # Examples
///
/// ```
/// use utoipa::{
///     openapi::{RefOr, Response, ResponseBuilder},
///     ToResponse,
/// };
///
/// struct MyResponse;
///
/// impl<'__r> ToResponse<'__r> for MyResponse {
///     fn response() -> (&'__r str, RefOr<Response>) {
///         (
///             "MyResponse",
///             ResponseBuilder::new().description("My Response").build().into(),
///         )
///     }
/// }
/// ```
///
/// [derive]: derive.ToResponse.html
pub trait ToResponse<'__r> {
    /// Returns a tuple of response component name (to be referenced) to a response.
    fn response() -> (&'__r str, openapi::RefOr<openapi::response::Response>);
}

/// Flexible number wrapper used by validation schema attributes to seamlessly support different
/// number syntaxes.
///
/// # Examples
///
/// _**Define object with two different number fields with minimum validation attribute.**_
///
/// ```rust
/// # use utoipa::Number;
/// # use utoipa::openapi::schema::{ObjectBuilder, SchemaType, Type};
/// let _ = ObjectBuilder::new()
///             .property("int_value", ObjectBuilder::new()
///                 .schema_type(Type::Integer).minimum(Some(1))
///             )
///             .property("float_value", ObjectBuilder::new()
///                 .schema_type(Type::Number).minimum(Some(-2.5))
///             )
///             .build();
/// ```
#[derive(Clone, serde::Deserialize, serde::Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[serde(untagged)]
pub enum Number {
    /// Signed integer e.g. `1` or `-2`
    Int(isize),
    /// Unsigned integer value e.g. `0`. Unsigned integer cannot be below zero.
    UInt(usize),
    /// Floating point number e.g. `1.34`
    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
);

/// Internal dev module used internally by utoipa-gen
#[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> ComposeSchema for std::collections::HashMap<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, 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> ComposeSchema for std::collections::HashSet<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<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)
        }
    }

    // For types not implementing `ToSchema`
    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);
        }
    }
}
