diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 51300bb1..e5b55697 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -63,10 +63,10 @@ jobs: if [[ "${{ matrix.testset }}" == "utoipa" ]] && [[ ${{ steps.changes.outputs.root_changed }} == true ]]; then cargo test --features uuid,openapi_extensions cargo test --test path_response_derive_test_no_serde_json --no-default-features - cargo test --test component_derive_no_serde_json --no-default-features + cargo test --test schema_derive_no_serde_json --no-default-features cargo test --test path_derive_actix --test path_parameter_derive_actix --features actix_extras - cargo test --test component_derive_test --features chrono,decimal,uuid - cargo test --test component_derive_test --features chrono_with_format + cargo test --test schema_derive_test --features chrono,decimal,uuid + cargo test --test schema_derive_test --features chrono_with_format cargo test --test path_derive_rocket --features rocket_extras,json cargo test --test path_derive_axum_test --features axum_extras,json cargo test --test utoipa_gen_test --features json,yaml diff --git a/README.md b/README.md index 8543e918..8f2cc48c 100644 --- a/README.md +++ b/README.md @@ -105,12 +105,12 @@ Please read the incoming changes from here: https://github.com/juhaku/utoipa/dis ## Examples -Create a struct or it could be an enum also. Add `Component` derive macro to it so it can be registered -as a component in OpenApi schema. +Create a struct or it could be an enum also. Add `ToSchema` derive macro to it so it can be registered +as an an OpenAPI schema. ```rust -use utoipa::Component; +use utoipa::ToSchema; -#[derive(Component)] +#[derive(ToSchema)] struct Pet { id: u64, name: String, @@ -145,12 +145,12 @@ mod pet_api { } ``` -Tie the `Component` and the endpoint above to the OpenApi schema with following `OpenApi` derive proc macro. +Tie the `Schema` and the endpoint above to the OpenApi schema with following `OpenApi` derive proc macro. ```rust use utoipa::OpenApi; #[derive(OpenApi)] -#[openapi(handlers(pet_api::get_pet_by_id), components(Pet))] +#[openapi(handlers(pet_api::get_pet_by_id), components(schemas(Pet)))] struct ApiDoc; println!("{}", ApiDoc::openapi().to_pretty_json().unwrap()); diff --git a/examples/rocket-todo/src/main.rs b/examples/rocket-todo/src/main.rs index 5d4e796c..5bd94883 100644 --- a/examples/rocket-todo/src/main.rs +++ b/examples/rocket-todo/src/main.rs @@ -82,12 +82,12 @@ mod todo { FromForm, Request, State, }; use serde::{Deserialize, Serialize}; - use utoipa::{Component, IntoParams}; + use utoipa::{ToSchema, IntoParams}; pub(super) type TodoStore = Arc>>; /// Todo operation error. - #[derive(Serialize, Component, Responder, Debug)] + #[derive(Serialize, ToSchema, Responder, Debug)] pub(super) enum TodoError { /// When there is conflict creating a new todo. #[response(status = 409)] @@ -144,13 +144,13 @@ mod todo { } /// Task to do. - #[derive(Serialize, Deserialize, Component, Clone)] + #[derive(Serialize, Deserialize, ToSchema, Clone)] pub(super) struct Todo { /// Unique todo id. - #[component(example = 1)] + #[schema(example = 1)] id: i32, /// Description of a taks. - #[component(example = "Buy groceries")] + #[schema(example = "Buy groceries")] value: String, /// Indicatation whether task is done or not. done: bool, diff --git a/examples/todo-actix/src/todo.rs b/examples/todo-actix/src/todo.rs index a5942fd8..95610f4a 100644 --- a/examples/todo-actix/src/todo.rs +++ b/examples/todo-actix/src/todo.rs @@ -6,7 +6,7 @@ use actix_web::{ HttpResponse, Responder, }; use serde::{Deserialize, Serialize}; -use utoipa::{Component, IntoParams}; +use utoipa::{ToSchema, IntoParams}; use crate::{LogApiKey, RequireApiKey}; @@ -29,30 +29,30 @@ pub(super) fn configure(store: Data) -> impl FnOnce(&mut ServiceConfi } /// Task to do. -#[derive(Serialize, Deserialize, Component, Clone, Debug)] +#[derive(Serialize, Deserialize, ToSchema, Clone, Debug)] pub(super) struct Todo { /// Unique id for the todo item. - #[component(example = 1)] + #[schema(example = 1)] id: i32, /// Description of the taks to do. - #[component(example = "Remember to buy groceries")] + #[schema(example = "Remember to buy groceries")] value: String, /// Mark is the task done or not checked: bool, } /// Request to update existing `Todo` item. -#[derive(Serialize, Deserialize, Component, Clone, Debug)] +#[derive(Serialize, Deserialize, ToSchema, Clone, Debug)] pub(super) struct TodoUpdateRequest { /// Optional new value for the `Todo` task. - #[component(example = "Dentist at 14.00")] + #[schema(example = "Dentist at 14.00")] value: Option, /// Optional check status to mark is the task done or not. checked: Option, } /// Todo endpoint error responses -#[derive(Serialize, Deserialize, Clone, Component)] +#[derive(Serialize, Deserialize, Clone, ToSchema)] pub(super) enum ErrorResponse { /// When Todo is not found by search term. NotFound(String), diff --git a/examples/todo-axum/src/main.rs b/examples/todo-axum/src/main.rs index d5522d06..bbf522d3 100644 --- a/examples/todo-axum/src/main.rs +++ b/examples/todo-axum/src/main.rs @@ -74,31 +74,31 @@ mod todo { use hyper::{HeaderMap, StatusCode}; use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; - use utoipa::{Component, IntoParams}; + use utoipa::{ToSchema, IntoParams}; /// In-memonry todo store pub(super) type Store = Mutex>; /// Item to do. - #[derive(Serialize, Deserialize, Component, Clone)] + #[derive(Serialize, Deserialize, ToSchema, Clone)] pub(super) struct Todo { id: i32, - #[component(example = "Buy groceries")] + #[schema(example = "Buy groceries")] value: String, done: bool, } /// Todo operation errors - #[derive(Serialize, Deserialize, Component)] + #[derive(Serialize, Deserialize, ToSchema)] pub(super) enum TodoError { /// Todo already exists conflict. - #[component(example = "Todo already exists")] + #[schema(example = "Todo already exists")] Conflict(String), /// Todo not found by id. - #[component(example = "id = 1")] + #[schema(example = "id = 1")] NotFound(String), /// Todo operation unauthorized - #[component(example = "missing api key")] + #[schema(example = "missing api key")] Unauthorized(String), } diff --git a/examples/todo-tide/src/main.rs b/examples/todo-tide/src/main.rs index 611c0bb3..7c2b80c1 100644 --- a/examples/todo-tide/src/main.rs +++ b/examples/todo-tide/src/main.rs @@ -89,23 +89,23 @@ mod todo { use serde::{Deserialize, Serialize}; use serde_json::json; use tide::{Request, Response}; - use utoipa::Component; + use utoipa::ToSchema; /// Item to complete - #[derive(Serialize, Deserialize, Component, Clone)] + #[derive(Serialize, Deserialize, ToSchema, Clone)] pub(super) struct Todo { /// Unique database id for `Todo` - #[component(example = 1)] + #[schema(example = 1)] id: i32, /// Description of task to complete - #[component(example = "Buy coffee")] + #[schema(example = "Buy coffee")] value: String, /// Indicates whether task is done or not done: bool, } /// Error that might occur when managing `Todo` items - #[derive(Serialize, Deserialize, Component)] + #[derive(Serialize, Deserialize, ToSchema)] pub(super) enum TodoError { /// Happens when Todo item alredy exists Config(String), diff --git a/examples/todo-warp/src/main.rs b/examples/todo-warp/src/main.rs index b252190d..bd6ced14 100644 --- a/examples/todo-warp/src/main.rs +++ b/examples/todo-warp/src/main.rs @@ -96,23 +96,23 @@ mod todo { }; use serde::{Deserialize, Serialize}; - use utoipa::{Component, IntoParams}; + use utoipa::{ToSchema, IntoParams}; use warp::{hyper::StatusCode, Filter, Reply}; pub type Store = Arc>>; /// Item to complete. - #[derive(Serialize, Deserialize, Component, Clone)] + #[derive(Serialize, Deserialize, ToSchema, Clone)] pub struct Todo { /// Unique database id. - #[component(example = 1)] + #[schema(example = 1)] id: i64, /// Description of what need to be done. - #[component(example = "Buy movie tickets")] + #[schema(example = "Buy movie tickets")] value: String, } - #[derive(Debug, Deserialize, Component)] + #[derive(Debug, Deserialize, ToSchema)] #[serde(rename_all = "snake_case")] pub enum Order { AscendingId, diff --git a/scripts/test.sh b/scripts/test.sh index ae8c5275..0c336676 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -9,10 +9,10 @@ echo "Testing crate: $crate..." if [[ "$crate" == "utoipa" ]]; then cargo test --features uuid cargo test --test path_response_derive_test_no_serde_json --no-default-features - cargo test --test component_derive_no_serde_json --no-default-features + cargo test --test schema_derive_no_serde_json --no-default-features cargo test --test path_derive_actix --test path_parameter_derive_actix --features actix_extras - cargo test --test component_derive_test --features chrono,decimal,uuid - cargo test --test component_derive_test --features chrono_with_format + cargo test --test schema_derive_test --features chrono,decimal,uuid + cargo test --test schema_derive_test --features chrono_with_format cargo test --test path_derive_rocket --features rocket_extras,json cargo test --test path_derive_axum_test --features axum_extras,json elif [[ "$crate" == "utoipa-gen" ]]; then diff --git a/src/lib.rs b/src/lib.rs index 6cb9956d..99688ea7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,18 +58,18 @@ //! * **chrono** Add support for [chrono](https://crates.io/crates/chrono) `DateTime`, `Date` and `Duration` types. By default these types //! are parsed to `string` types without additional format. If you want to have formats added to the types use _chrono_with_format_ feature. //! This is useful because OpenAPI 3.1 spec does not have date-time formats. To override default `string` representation -//! users have to use `value_type` attribute to override the type. See [docs](https://docs.rs/utoipa/1.1.0/utoipa/derive.Component.html) for more details. +//! users have to use `value_type` attribute to override the type. See [docs](https://docs.rs/utoipa/1.1.0/utoipa/derive.ToSchema.html) for more details. //! * **chrono_with_format** Add support to [chrono](https://crates.io/crates/chrono) types described above with additional `format` //! information type. `date-time` for `DateTime` and `date` for `Date` 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/1.1.0/utoipa/derive.Component.html) for more details. +//! users have to use `value_type` attribute to override the type. See [docs](https://docs.rs/utoipa/1.1.0/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/1.1.0/utoipa/derive.Component.html) for more details. +//! to override the type. See [docs](https://docs.rs/utoipa/1.1.0/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 [component derive docs][component_derive]. +//! See the `value_type` in [`ToSchema` derive docs][to_schema_derive]. //! * **uuid** Add support for [uuid](https://github.com/uuid-rs/uuid). `Uuid` type will be presented as `String` with //! format `uuid` in OpenAPI spec. //! * **smallvec** Add support for [smallvec](https://crates.io/crates/smallvec). `SmallVec` will be treated as `Vec`. @@ -77,7 +77,7 @@ //! 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. //! -//! Utoipa implicitly has partial support for `serde` attributes. See [component derive][serde] for more details. +//! Utoipa implicitly has partial support for `serde` attributes. See [`ToSchema` derive][serde] for more details. //! //! # Install //! @@ -100,11 +100,11 @@ //! //! # Examples //! -//! Create a struct or it could be an enum also. Add `Component` derive macro to it so it can be registered +//! Create a struct or it could be an enum also. Add `ToSchema` derive macro to it so it can be registered //! as a component in openapi schema. //! ```rust -//! use utoipa::Component; -//! #[derive(Component)] +//! use utoipa::ToSchema; +//! #[derive(ToSchema)] //! struct Pet { //! id: u64, //! name: String, @@ -116,9 +116,9 @@ //! ```rust //! mod pet_api { //! # use utoipa::OpenApi; -//! # use utoipa::Component; +//! # use utoipa::ToSchema; //! # -//! # #[derive(Component)] +//! # #[derive(ToSchema)] //! # struct Pet { //! # id: u64, //! # name: String, @@ -151,9 +151,9 @@ //! Tie the component and the above api to the openapi schema with following `OpenApi` derive proc macro. //! ```rust //! # mod pet_api { -//! # use utoipa::Component; +//! # use utoipa::ToSchema; //! # -//! # #[derive(Component)] +//! # #[derive(ToSchema)] //! # struct Pet { //! # id: u64, //! # name: String, @@ -182,9 +182,9 @@ //! # } //! # } //! # } -//! # use utoipa::Component; +//! # use utoipa::ToSchema; //! # -//! # #[derive(Component)] +//! # #[derive(ToSchema)] //! # struct Pet { //! # id: u64, //! # name: String, @@ -192,7 +192,7 @@ //! # } //! # use utoipa::OpenApi; //! #[derive(OpenApi)] -//! #[openapi(handlers(pet_api::get_pet_by_id), components(Pet))] +//! #[openapi(handlers(pet_api::get_pet_by_id), components(schemas(Pet)))] //! struct ApiDoc; //! //! println!("{}", ApiDoc::openapi().to_pretty_json().unwrap()); @@ -208,10 +208,10 @@ //! [rocket_path]: attr.path.html#rocket_extras-support-for-rocket //! [actix_path]: attr.path.html#actix_extras-support-for-actix-web //! [axum_path]: attr.path.html#axum_extras-suppport-for-axum -//! [serde]: derive.Component.html#partial-serde-attributes-support +//! [serde]: derive.ToSchema.html#partial-serde-attributes-support //! //! [security]: openapi/security/index.html -//! [component_derive]: derive.Component.html +//! [to_schema_derive]: derive.ToSchema.html pub mod openapi; @@ -224,7 +224,7 @@ pub use utoipa_gen::*; /// /// 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(Component)]` refer to [derive documentation][derive]. +/// `#[derive(ToSchema)]` refer to [derive documentation][derive]. /// /// # Examples /// @@ -245,7 +245,7 @@ pub use utoipa_gen::*; /// /// impl utoipa::OpenApi for OpenApiDoc { /// fn openapi() -> utoipa::openapi::OpenApi { -/// use utoipa::{Component, Path}; +/// use utoipa::{ToSchema, Path}; /// utoipa::openapi::OpenApiBuilder::new() /// .info(utoipa::openapi::InfoBuilder::new() /// .title("application name") @@ -271,17 +271,17 @@ pub trait OpenApi { /// Trait for implementing OpenAPI Schema object. /// /// This trait is derivable and can be used with `[#derive]` attribute. For a details of -/// `#[derive(Component)]` refer to [derive documentation][derive]. +/// `#[derive(ToSchema)]` refer to [derive documentation][derive]. /// -/// [derive]: derive.Component.html +/// [derive]: derive.ToSchema.html /// /// # Examples /// -/// Use `#[derive]` to implement `Component` trait. +/// Use `#[derive]` to implement `ToSchema` trait. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] -/// #[component(example = json!({"name": "bob the cat", "id": 1}))] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] +/// #[schema(example = json!({"name": "bob the cat", "id": 1}))] /// struct Pet { /// id: u64, /// name: String, @@ -297,27 +297,27 @@ pub trait OpenApi { /// # age: Option, /// # } /// # -/// impl utoipa::Component for Pet { -/// fn component() -> utoipa::openapi::schema::Component { +/// impl utoipa::ToSchema for Pet { +/// fn schema() -> utoipa::openapi::schema::Schema { /// use utoipa::openapi::ToArray; /// utoipa::openapi::ObjectBuilder::new() /// .property( /// "id", /// utoipa::openapi::PropertyBuilder::new() -/// .component_type(utoipa::openapi::ComponentType::Integer) -/// .format(Some(utoipa::openapi::ComponentFormat::Int64)), +/// .schema_type(utoipa::openapi::SchemaType::Integer) +/// .format(Some(utoipa::openapi::SchemaFormat::Int64)), /// ) /// .required("id") /// .property( /// "name", -/// utoipa::openapi::Property::new(utoipa::openapi::ComponentType::String), +/// utoipa::openapi::Property::new(utoipa::openapi::SchemaType::String), /// ) /// .required("name") /// .property( /// "age", /// utoipa::openapi::PropertyBuilder::new() -/// .component_type(utoipa::openapi::ComponentType::Integer) -/// .format(Some(utoipa::openapi::ComponentFormat::Int32)), +/// .schema_type(utoipa::openapi::SchemaType::Integer) +/// .format(Some(utoipa::openapi::SchemaFormat::Int32)), /// ) /// .example(Some(serde_json::json!({ /// "name": "bob the cat", "id": 1 @@ -326,10 +326,10 @@ pub trait OpenApi { /// } /// } /// ``` -pub trait Component { - fn component() -> openapi::schema::Component; +pub trait ToSchema { + fn schema() -> openapi::schema::Schema; - fn aliases() -> Vec<(&'static str, openapi::schema::Component)> { + fn aliases() -> Vec<(&'static str, openapi::schema::Schema)> { Vec::new() } } @@ -386,7 +386,7 @@ pub trait Component { /// .description("Pet found successfully") /// .content("application/json", /// utoipa::openapi::Content::new( -/// utoipa::openapi::Ref::from_component_name("Pet"), +/// utoipa::openapi::Ref::from_schema_name("Pet"), /// ), /// ), /// ) @@ -405,8 +405,8 @@ pub trait Component { /// .description(Some("Pet database id to get Pet for")) /// .schema( /// Some(utoipa::openapi::PropertyBuilder::new() -/// .component_type(utoipa::openapi::ComponentType::Integer) -/// .format(Some(utoipa::openapi::ComponentFormat::Int64))), +/// .schema_type(utoipa::openapi::SchemaType::Integer) +/// .format(Some(utoipa::openapi::SchemaFormat::Int64))), /// ), /// ) /// .tag("pet_api"), @@ -530,8 +530,8 @@ pub trait Modify { /// .description(Some("Id of pet")) /// .schema(Some( /// utoipa::openapi::PropertyBuilder::new() -/// .component_type(utoipa::openapi::ComponentType::Integer) -/// .format(Some(utoipa::openapi::ComponentFormat::Int64)), +/// .schema_type(utoipa::openapi::SchemaType::Integer) +/// .format(Some(utoipa::openapi::SchemaFormat::Int64)), /// )) /// .build(), /// utoipa::openapi::path::ParameterBuilder::new() @@ -541,7 +541,7 @@ pub trait Modify { /// .description(Some("Name of pet")) /// .schema(Some( /// utoipa::openapi::PropertyBuilder::new() -/// .component_type(utoipa::openapi::ComponentType::String), +/// .schema_type(utoipa::openapi::SchemaType::String), /// )) /// .build(), /// ] diff --git a/src/openapi.rs b/src/openapi.rs index c930eecd..ec615a0d 100644 --- a/src/openapi.rs +++ b/src/openapi.rs @@ -10,9 +10,8 @@ pub use self::{ path::{PathItem, PathItemType, Paths, PathsBuilder}, response::{Response, ResponseBuilder, Responses, ResponsesBuilder}, schema::{ - Array, ArrayBuilder, Component, ComponentFormat, ComponentType, Components, - ComponentsBuilder, Object, ObjectBuilder, OneOf, OneOfBuilder, Property, PropertyBuilder, - Ref, ToArray, + Array, ArrayBuilder, Components, ComponentsBuilder, Object, ObjectBuilder, OneOf, + OneOfBuilder, Property, PropertyBuilder, Ref, Schema, SchemaFormat, SchemaType, ToArray, }, security::SecurityRequirement, server::{Server, ServerBuilder, ServerVariable, ServerVariableBuilder}, diff --git a/src/openapi/content.rs b/src/openapi/content.rs index 5660ec66..c1cc0682 100644 --- a/src/openapi/content.rs +++ b/src/openapi/content.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "serde_json")] use serde_json::Value; -use super::{build_fn, encoding::Encoding, from, new, set_value, Component}; +use super::{build_fn, encoding::Encoding, from, new, set_value, Schema}; /// Content holds request body content or response content. #[derive(Serialize, Deserialize, Default, Clone)] @@ -14,7 +14,7 @@ use super::{build_fn, encoding::Encoding, from, new, set_value, Component}; #[non_exhaustive] pub struct Content { /// Schema used in response body or request body. - pub schema: Component, + pub schema: Schema, /// Example for request body or response body. #[serde(skip_serializing_if = "Option::is_none")] @@ -29,7 +29,7 @@ pub struct Content { /// A map between a property name and its encoding information. /// /// The key, being the property name, MUST exist in the [`Content::schema`] as a property, with - /// `schema` being a [`Component::Object`] and this object containing the same property key in + /// `schema` being a [`Schema::Object`] and this object containing the same property key in /// [`Object::properties`](crate::openapi::schema::Object::properties). /// /// The encoding object SHALL only apply to `request_body` objects when the media type is @@ -39,7 +39,7 @@ pub struct Content { } impl Content { - pub fn new>(schema: I) -> Self { + pub fn new>(schema: I) -> Self { Self { schema: schema.into(), ..Self::default() @@ -50,7 +50,7 @@ impl Content { /// Builder for [`Content`] with chainable configuration methods to create a new [`Content`]. #[derive(Default)] pub struct ContentBuilder { - schema: Component, + schema: Schema, #[cfg(feature = "serde_json")] example: Option, @@ -67,7 +67,7 @@ impl ContentBuilder { new!(pub ContentBuilder); /// Add schema. - pub fn schema>(mut self, component: I) -> Self { + pub fn schema>(mut self, component: I) -> Self { set_value!(self schema component.into()) } @@ -86,7 +86,7 @@ impl ContentBuilder { /// Add an encoding. /// /// The `property_name` MUST exist in the [`Content::schema`] as a property, - /// with `schema` being a [`Component::Object`] and this object containing the same property + /// with `schema` being a [`Schema::Object`] and this object containing the same property /// key in [`Object::properties`](crate::openapi::schema::Object::properties). /// /// The encoding object SHALL only apply to `request_body` objects when the media type is diff --git a/src/openapi/header.rs b/src/openapi/header.rs index 0100def7..142b6bb4 100644 --- a/src/openapi/header.rs +++ b/src/openapi/header.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; -use super::{build_fn, builder, from, new, set_value, Component, ComponentType, Property}; +use super::{build_fn, builder, from, new, set_value, Property, Schema, SchemaType}; builder! { HeaderBuilder; @@ -17,7 +17,7 @@ builder! { #[cfg_attr(feature = "debug", derive(Debug))] pub struct Header { /// Schema of header type. - pub schema: Component, + pub schema: Schema, /// Additional descripiton of the header value. #[serde(skip_serializing_if = "Option::is_none")] @@ -34,17 +34,16 @@ impl Header { /// Create new [`Header`] with integer type. /// ```rust /// # use utoipa::openapi::header::Header; - /// # use utoipa::openapi::{Property, ComponentType}; - /// let header = Header::new(Property::new(ComponentType::Integer)); + /// # use utoipa::openapi::{Property, SchemaType}; + /// let header = Header::new(Property::new(SchemaType::Integer)); /// ``` /// /// Create a new [`Header`] with default type `String` /// ```rust /// # use utoipa::openapi::header::Header; - /// # use utoipa::openapi::{Property, ComponentType}; /// let header = Header::default(); /// ``` - pub fn new>(component: C) -> Self { + pub fn new>(component: C) -> Self { Self { schema: component.into(), ..Default::default() @@ -56,14 +55,14 @@ impl Default for Header { fn default() -> Self { Self { description: Default::default(), - schema: Property::new(ComponentType::String).into(), + schema: Property::new(SchemaType::String).into(), } } } impl HeaderBuilder { /// Add schema of header. - pub fn schema>(mut self, component: I) -> Self { + pub fn schema>(mut self, component: I) -> Self { set_value!(self schema component.into()) } diff --git a/src/openapi/path.rs b/src/openapi/path.rs index 5ef6de9c..3edf6843 100644 --- a/src/openapi/path.rs +++ b/src/openapi/path.rs @@ -11,7 +11,7 @@ use super::{ build_fn, builder, from, new, request_body::RequestBody, response::{Response, Responses}, - set_value, Component, Deprecated, ExternalDocs, Required, SecurityRequirement, Server, + set_value, Deprecated, ExternalDocs, Required, Schema, SecurityRequirement, Server, }; builder! { @@ -439,9 +439,9 @@ pub struct Parameter { #[serde(skip_serializing_if = "Option::is_none")] pub deprecated: Option, // pub allow_empty_value: bool, this is going to be removed from further open api spec releases - /// Schema of the parameter. Typically [`Component::Property`] is used. + /// Schema of the parameter. Typically [`Schema::Property`] is used. #[serde(skip_serializing_if = "Option::is_none")] - pub schema: Option, + pub schema: Option, /// Describes how [`Parameter`] is being serialized depending on [`Parameter::schema`] (type of a content). /// Default value is based on [`ParameterIn`]. @@ -506,7 +506,7 @@ pub struct ParameterBuilder { deprecated: Option, - schema: Option, + schema: Option, style: Option, @@ -558,7 +558,7 @@ impl ParameterBuilder { } /// Add or change [`Parameter`]s schema. - pub fn schema>(mut self, component: Option) -> Self { + pub fn schema>(mut self, component: Option) -> Self { set_value!(self schema component.map(|component| component.into())) } diff --git a/src/openapi/request_body.rs b/src/openapi/request_body.rs index d5b13964..bce3270f 100644 --- a/src/openapi/request_body.rs +++ b/src/openapi/request_body.rs @@ -66,7 +66,7 @@ impl RequestBodyBuilder { /// ``` /// /// Once enabled, with a single method call we can add [`Content`] to our RequestBodyBuilder -/// that references a [`crate::Component`] schema using content-tpe "application/json": +/// that references a [`crate::ToSchema`] schema using content-tpe "application/json": /// /// ```rust /// use utoipa::openapi::request_body::{RequestBodyBuilder, RequestBodyExt}; @@ -100,7 +100,7 @@ impl RequestBodyExt for RequestBody { fn json_component_ref(mut self, ref_name: &str) -> RequestBody { self.content.insert( "application/json".to_string(), - crate::openapi::Content::new(crate::openapi::Ref::from_component_name(ref_name)), + crate::openapi::Content::new(crate::openapi::Ref::from_schema_name(ref_name)), ); self } @@ -111,7 +111,7 @@ impl RequestBodyExt for RequestBodyBuilder { fn json_component_ref(self, ref_name: &str) -> RequestBodyBuilder { self.content( "application/json", - crate::openapi::Content::new(crate::openapi::Ref::from_component_name(ref_name)), + crate::openapi::Content::new(crate::openapi::Ref::from_schema_name(ref_name)), ) } } @@ -139,7 +139,7 @@ mod tests { .required(Some(Required::True)) .content( "application/json", - Content::new(crate::openapi::Ref::from_component_name("EmailPayload")), + Content::new(crate::openapi::Ref::from_schema_name("EmailPayload")), ) .build(); let serialized = serde_json::to_string_pretty(&request_body)?; diff --git a/src/openapi/response.rs b/src/openapi/response.rs index 5c1bd8ba..2b508ebc 100644 --- a/src/openapi/response.rs +++ b/src/openapi/response.rs @@ -190,7 +190,7 @@ impl ResponseExt for Response { fn json_component_ref(mut self, ref_name: &str) -> Response { self.content.insert( "application/json".to_string(), - Content::new(crate::openapi::Ref::from_component_name(ref_name)), + Content::new(crate::openapi::Ref::from_schema_name(ref_name)), ); self } @@ -209,7 +209,7 @@ impl ResponseExt for ResponseBuilder { fn json_component_ref(self, ref_name: &str) -> ResponseBuilder { self.content( "application/json", - Content::new(crate::openapi::Ref::from_component_name(ref_name)), + Content::new(crate::openapi::Ref::from_schema_name(ref_name)), ) } diff --git a/src/openapi/schema.rs b/src/openapi/schema.rs index 490afc78..1a01c591 100644 --- a/src/openapi/schema.rs +++ b/src/openapi/schema.rs @@ -16,7 +16,7 @@ use crate::ToResponse; macro_rules! component_from_builder { ( $name:ident ) => { - impl From<$name> for Component { + impl From<$name> for Schema { fn from(builder: $name) -> Self { builder.build().into() } @@ -49,7 +49,7 @@ builder! { /// /// [schema]: https://spec.openapis.org/oas/latest.html#schema-object #[serde(skip_serializing_if = "BTreeMap::is_empty", default)] - pub schemas: BTreeMap, + pub schemas: BTreeMap>, /// Map of reusable response name, to [OpenAPI Response Object][response]s or [OpenAPI /// Reference][reference]s to [OpenAPI Response Object][response]s. @@ -112,43 +112,45 @@ impl Components { } impl ComponentsBuilder { - /// Add [`Component`] to [`Components`]. + /// Add [`Schema`] to [`Components`]. /// /// Accpets two arguments where first is name of the schema and second is the schema itself. - pub fn schema, I: Into>(mut self, name: S, component: I) -> Self { - self.schemas.insert(name.into(), component.into()); + pub fn schema, I: Into>>(mut self, name: S, schema: I) -> Self { + self.schemas.insert(name.into(), schema.into()); self } - /// Add [`Component`]s from iterator. + /// Add [`Schema`]s from iterator. /// /// # Examples /// ```rust /// # use utoipa::openapi::schema::{ComponentsBuilder, ObjectBuilder, - /// # PropertyBuilder, ComponentType}; + /// # PropertyBuilder, SchemaType, Schema}; /// ComponentsBuilder::new().schemas_from_iter([( /// "Pet", - /// ObjectBuilder::new() - /// .property( - /// "name", - /// PropertyBuilder::new().component_type(ComponentType::String), - /// ) - /// .required("name"), + /// Schema::from( + /// ObjectBuilder::new() + /// .property( + /// "name", + /// PropertyBuilder::new().schema_type(SchemaType::String), + /// ) + /// .required("name") + /// ), /// )]); /// ``` pub fn schemas_from_iter< I: IntoIterator, - C: Into, + C: Into>, S: Into, >( mut self, - components: I, + schemas: I, ) -> Self { self.schemas.extend( - components + schemas .into_iter() - .map(|(name, component)| (name.into(), component.into())), + .map(|(name, schema)| (name.into(), schema.into())), ); self @@ -203,41 +205,40 @@ impl ComponentsBuilder { } } -/// Is super type for [OpenAPI Schema Object][components] components. Component -/// is reusable resource what can be referenced from path operations and other -/// components using [`Ref`] component. +/// Is super type for [OpenAPI Schema Object][schemas]. Schema is reusable resource what can be +/// referenced from path operations and other components using [`Ref`] component. /// -/// [components]: https://spec.openapis.org/oas/latest.html#components-object +/// [schemas]: https://spec.openapis.org/oas/latest.html#schema-object #[non_exhaustive] #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(feature = "debug", derive(Debug))] #[serde(untagged, rename_all = "camelCase")] -pub enum Component { +pub enum Schema { /// Defines object component. This is formed from structs holding [`Property`] components /// created from it's fields. Object(Object), /// Defines property component typically used together with - /// [`Component::Object`] or [`Component::Array`]. It is used to map + /// [`Schema::Object`] or [`Schema::Array`]. It is used to map /// field types to OpenAPI documentation. Property(Property), - /// Creates a reference component _`$ref=#/components/schemas/ComponentName`_. Which + /// Creates a reference component _`$ref=#/components/schemas/SchemaName`_. Which /// can be used to reference a other reusable component in [`Components`]. Ref(Ref), /// Defines array component from another component. Typically used with - /// [`Component::Property`] or [`Component::Object`] component. Slice and Vec - /// types are translated to [`Component::Array`] types. + /// [`Schema::Property`] or [`Schema::Object`] component. Slice and Vec + /// types are translated to [`Schema::Array`] types. Array(Array), /// Creates a _OneOf_ type [Discriminator Object][discriminator] component. This component /// is used to map multiple components together where API endpoint could return any of them. - /// [`Component::OneOf`] is created form complex enum where enum holds other than unit types. + /// [`Schema::OneOf`] is created form complex enum where enum holds other than unit types. /// /// [discriminator]: https://spec.openapis.org/oas/latest.html#components-object OneOf(OneOf), } -impl Default for Component { +impl Default for Schema { fn default() -> Self { - Component::Object(Object::default()) + Schema::Object(Object::default()) } } @@ -247,7 +248,7 @@ builder! { /// OneOf [Discriminator Object][discriminator] component holds /// multiple components together where API endpoint could return any of them. /// - /// See [`Component::OneOf`] for more details. + /// See [`Schema::OneOf`] for more details. /// /// [discriminator]: https://spec.openapis.org/oas/latest.html#components-object #[derive(Serialize, Deserialize, Clone, Default)] @@ -255,7 +256,7 @@ builder! { pub struct OneOf { /// Components of _OneOf_ component. #[serde(rename = "oneOf")] - pub items: Vec, + pub items: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, @@ -291,10 +292,10 @@ impl OneOf { } impl OneOfBuilder { - /// Adds a given [`Component`] to [`OneOf`] [Discriminator Object][discriminator] + /// Adds a given [`Schema`] to [`OneOf`] [Discriminator Object][discriminator] /// /// [discriminator]: https://spec.openapis.org/oas/latest.html#components-object - pub fn item>(mut self, component: I) -> Self { + pub fn item>(mut self, component: I) -> Self { self.items.push(component.into()); self @@ -308,7 +309,7 @@ impl OneOfBuilder { to_array_builder!(); } -impl From for Component { +impl From for Schema { fn from(one_of: OneOf) -> Self { Self::OneOf(one_of) } @@ -324,17 +325,17 @@ component_from_builder!(OneOfBuilder); #[cfg_attr(feature = "debug", derive(Debug))] #[serde(rename_all = "camelCase")] pub struct Property { - /// Type of the property e.g [`ComponentType::String`]. + /// Type of the property e.g [`SchemaType::String`]. #[serde(rename = "type")] - pub component_type: ComponentType, + pub schema_type: SchemaType, /// Changes the [`Property`] title. #[serde(skip_serializing_if = "Option::is_none")] title: Option, - /// Additional format for detailing the component type. + /// Additional format for detailing the schema type. #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, + pub format: Option, /// Description of the property. Markdown syntax is supported. #[serde(skip_serializing_if = "Option::is_none")] @@ -382,15 +383,15 @@ pub struct Property { } impl Property { - pub fn new(component_type: ComponentType) -> Self { + pub fn new(schema_type: SchemaType) -> Self { Self { - component_type, + schema_type, ..Default::default() } } } -impl From for Component { +impl From for Schema { fn from(property: Property) -> Self { Self::Property(property) } @@ -401,11 +402,11 @@ impl ToArray for Property {} /// Builder for [`Property`] with chainable configuration methods to create a new [`Property`]. #[derive(Default)] pub struct PropertyBuilder { - component_type: ComponentType, + schema_type: SchemaType, title: Option, - format: Option, + format: Option, description: Option, @@ -433,14 +434,14 @@ pub struct PropertyBuilder { } from!(Property PropertyBuilder - component_type, title, format, description, default, enum_values, example, deprecated, write_only, read_only, xml); + schema_type, title, format, description, default, enum_values, example, deprecated, write_only, read_only, xml); impl PropertyBuilder { new!(pub PropertyBuilder); - /// Add or change type of the property e.g [`ComponentType::String`]. - pub fn component_type(mut self, component_type: ComponentType) -> Self { - set_value!(self component_type component_type) + /// Add or change type of the property e.g [`SchemaType::String`]. + pub fn schema_type(mut self, schema_type: SchemaType) -> Self { + set_value!(self schema_type schema_type) } /// Add or change the title of the [`Property`]. @@ -449,7 +450,7 @@ impl PropertyBuilder { } /// Add or change additional format for detailing the component type. - pub fn format(mut self, format: Option) -> Self { + pub fn format(mut self, format: Option) -> Self { set_value!(self format format) } @@ -514,13 +515,13 @@ impl PropertyBuilder { to_array_builder!(); build_fn!(pub Property - component_type, title, format, description, default, enum_values, example, deprecated, write_only, read_only, xml); + schema_type, title, format, description, default, enum_values, example, deprecated, write_only, read_only, xml); } component_from_builder!(PropertyBuilder); /// Implements subset of [OpenAPI Schema Object][schema] which allows -/// adding other [`Component`]s as **properties** to this [`Component`]. +/// adding other [`Schema`]s as **properties** to this [`Schema`]. /// /// [schema]: https://spec.openapis.org/oas/latest.html#schema-object #[non_exhaustive] @@ -528,9 +529,9 @@ component_from_builder!(PropertyBuilder); #[cfg_attr(feature = "debug", derive(Debug))] #[serde(rename_all = "camelCase")] pub struct Object { - /// Data type of [`Object`]. Will always be [`ComponentType::Object`] + /// Data type of [`Object`]. Will always be [`SchemaType::Object`] #[serde(rename = "type")] - component_type: ComponentType, + schema_type: SchemaType, /// Changes the [`Object`] title. #[serde(skip_serializing_if = "Option::is_none")] @@ -540,13 +541,13 @@ pub struct Object { #[serde(skip_serializing_if = "Vec::is_empty")] pub required: Vec, - /// Map of fields with their [`Component`] types. + /// Map of fields with their [`Schema`] types. #[serde(skip_serializing_if = "BTreeMap::is_empty")] - pub properties: BTreeMap, + pub properties: BTreeMap, - /// Additional [`Component`] for non specified fields (Useful for typed maps). + /// Additional [`Schema`] for non specified fields (Useful for typed maps). #[serde(skip_serializing_if = "Option::is_none")] - pub additional_properties: Option>, + pub additional_properties: Option>, /// Description of the [`Object`]. Markdown syntax is supported. #[serde(skip_serializing_if = "Option::is_none")] @@ -579,7 +580,7 @@ impl Object { } } -impl From for Component { +impl From for Schema { fn from(s: Object) -> Self { Self::Object(s) } @@ -590,15 +591,15 @@ impl ToArray for Object {} /// Builder for [`Object`] with chainable configuration methods to create a new [`Object`]. #[derive(Default)] pub struct ObjectBuilder { - component_type: ComponentType, + schema_type: SchemaType, title: Option, required: Vec, - properties: BTreeMap, + properties: BTreeMap, - additional_properties: Option>, + additional_properties: Option>, description: Option, @@ -619,7 +620,7 @@ impl ObjectBuilder { /// Add new property to the [`Object`]. /// /// Method accepts property name and property component as an arguments. - pub fn property, I: Into>( + pub fn property, I: Into>( mut self, property_name: S, component: I, @@ -630,7 +631,7 @@ impl ObjectBuilder { self } - pub fn additional_properties>( + pub fn additional_properties>( mut self, additional_properties: Option, ) -> Self { @@ -678,10 +679,10 @@ impl ObjectBuilder { to_array_builder!(); - build_fn!(pub Object component_type, title, required, properties, description, deprecated, example, xml, additional_properties); + build_fn!(pub Object schema_type, title, required, properties, description, deprecated, example, xml, additional_properties); } -from!(Object ObjectBuilder component_type, title, required, properties, description, deprecated, example, xml, additional_properties); +from!(Object ObjectBuilder schema_type, title, required, properties, description, deprecated, example, xml, additional_properties); component_from_builder!(ObjectBuilder); /// Implements [OpenAPI Reference Object][reference] that can be used to reference @@ -699,17 +700,17 @@ pub struct Ref { impl Ref { /// Construct a new [`Ref`] with custom ref location. In most cases this is not necessary - /// and [`Ref::from_component_name`] could be used instead. + /// and [`Ref::from_schema_name`] could be used instead. pub fn new>(ref_location: I) -> Self { Self { ref_location: ref_location.into(), } } - /// Construct a new [`Ref`] from provided component name. This will create a [`Ref`] that + /// Construct a new [`Ref`] from provided schema name. This will create a [`Ref`] that /// references the the reusable schemas. - pub fn from_component_name>(component_name: I) -> Self { - Self::new(&format!("#/components/schemas/{}", component_name.into())) + pub fn from_schema_name>(schema_name: I) -> Self { + Self::new(&format!("#/components/schemas/{}", schema_name.into())) } /// Construct a new [`Ref`] from provided response name. This will create a [`Ref`] that @@ -721,7 +722,7 @@ impl Ref { to_array_builder!(); } -impl From for Component { +impl From for Schema { fn from(r: Ref) -> Self { Self::Ref(r) } @@ -746,20 +747,20 @@ impl From for RefOr { builder! { ArrayBuilder; - /// Component represents [`Vec`] or [`slice`] type of items. + /// Array represents [`Vec`] or [`slice`] type of items. /// - /// See [`Component::Array`] for more details. + /// See [`Schema::Array`] for more details. #[non_exhaustive] #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(feature = "debug", derive(Debug))] #[serde(rename_all = "camelCase")] pub struct Array { - /// Type will always be [`ComponentType::Array`] + /// Type will always be [`SchemaType::Array`] #[serde(rename = "type")] - component_type: ComponentType, + schema_type: SchemaType, - /// Component representing the array items type. - pub items: Box, + /// Schema representing the array items type. + pub items: Box, /// Max length of the array. #[serde(skip_serializing_if = "Option::is_none")] @@ -778,7 +779,7 @@ builder! { impl Default for Array { fn default() -> Self { Self { - component_type: ComponentType::Array, + schema_type: SchemaType::Array, items: Default::default(), max_items: Default::default(), min_items: Default::default(), @@ -788,16 +789,16 @@ impl Default for Array { } impl Array { - /// Construct a new [`Array`] component from given [`Component`]. + /// Construct a new [`Array`] component from given [`Schema`]. /// /// # Examples /// /// Create a `String` array component. /// ```rust - /// # use utoipa::openapi::schema::{Component, Array, ComponentType, Property}; - /// let string_array = Array::new(Property::new(ComponentType::String)); + /// # use utoipa::openapi::schema::{Schema, Array, SchemaType, Property}; + /// let string_array = Array::new(Property::new(SchemaType::String)); /// ``` - pub fn new>(component: I) -> Self { + pub fn new>(component: I) -> Self { Self { items: Box::new(component.into()), ..Default::default() @@ -811,8 +812,8 @@ impl Array { } impl ArrayBuilder { - /// Set [`Component`] type for the [`Array`]. - pub fn items>(mut self, component: I) -> Self { + /// Set [`Schema`] type for the [`Array`]. + pub fn items>(mut self, component: I) -> Self { set_value!(self items Box::new(component.into())) } @@ -836,7 +837,7 @@ impl ArrayBuilder { component_from_builder!(ArrayBuilder); -impl From for Component { +impl From for Schema { fn from(array: Array) -> Self { Self::Array(array) } @@ -846,7 +847,7 @@ impl ToArray for Array {} pub trait ToArray where - Component: From, + Schema: From, Self: Sized, { fn to_array(self) -> Array { @@ -854,13 +855,13 @@ where } } -/// Represents data type of [`Component`]. +/// Represents data type of [`Schema`]. #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(feature = "debug", derive(Debug))] #[serde(rename_all = "lowercase")] -pub enum ComponentType { +pub enum SchemaType { /// Used with [`Object`] and [`ObjectBuilder`]. Objects always have - /// _component_type_ [`ComponentType::Object`]. + /// _schema_type_ [`SchemaType::Object`]. Object, /// Indicates string type of content. Typically used with [`Property`] and [`PropertyBuilder`]. String, @@ -875,18 +876,18 @@ pub enum ComponentType { Array, } -impl Default for ComponentType { +impl Default for SchemaType { fn default() -> Self { Self::Object } } -/// Additional format for [`ComponentType`] to fine tune the data type used. If the **format** is not -/// supported by the UI it may default back to [`ComponentType`] alone. +/// Additional format for [`SchemaType`] to fine tune the data type used. If the **format** is not +/// supported by the UI it may default back to [`SchemaType`] alone. #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(feature = "debug", derive(Debug))] #[serde(rename_all = "lowercase")] -pub enum ComponentFormat { +pub enum SchemaFormat { /// 32 bit integer. Int32, /// 64 bit integer. @@ -929,42 +930,44 @@ mod tests { .paths(Paths::new()) .components(Some( ComponentsBuilder::new() - .schema("Person", Ref::new("#/components/PersonModel")) + .schema("Person", RefOr::Ref(Ref::new("#/components/PersonModel"))) .schema( "Credential", - ObjectBuilder::new() - .property( - "id", - PropertyBuilder::new() - .component_type(ComponentType::Integer) - .format(Some(ComponentFormat::Int32)) - .description(Some("Id of credential")) - .default(Some(json!(1i32))), - ) - .property( - "name", - PropertyBuilder::new() - .component_type(ComponentType::String) - .description(Some("Name of credential")), - ) - .property( - "status", - PropertyBuilder::new() - .component_type(ComponentType::String) - .default(Some(json!("Active"))) - .description(Some("Credential status")) - .enum_values(Some([ - "Active", - "NotActive", - "Locked", - "Expired", - ])), - ) - .property( - "history", - Array::new(Ref::from_component_name("UpdateHistory")), - ) - .property("tags", Property::new(ComponentType::String).to_array()), + Schema::from( + ObjectBuilder::new() + .property( + "id", + PropertyBuilder::new() + .schema_type(SchemaType::Integer) + .format(Some(SchemaFormat::Int32)) + .description(Some("Id of credential")) + .default(Some(json!(1i32))), + ) + .property( + "name", + PropertyBuilder::new() + .schema_type(SchemaType::String) + .description(Some("Name of credential")), + ) + .property( + "status", + PropertyBuilder::new() + .schema_type(SchemaType::String) + .default(Some(json!("Active"))) + .description(Some("Credential status")) + .enum_values(Some([ + "Active", + "NotActive", + "Locked", + "Expired", + ])), + ) + .property( + "history", + Array::new(Ref::from_schema_name("UpdateHistory")), + ) + .property("tags", Property::new(SchemaType::String).to_array()), + ), ) .build(), )) @@ -1038,9 +1041,7 @@ mod tests { #[test] fn test_additional_properties() { let json_value = ObjectBuilder::new() - .additional_properties(Some( - PropertyBuilder::new().component_type(ComponentType::String), - )) + .additional_properties(Some(PropertyBuilder::new().schema_type(SchemaType::String))) .build(); assert_json_eq!( json_value, @@ -1053,7 +1054,7 @@ mod tests { ); let json_value = ObjectBuilder::new() - .additional_properties(Some(Ref::from_component_name("ComplexModel"))) + .additional_properties(Some(Ref::from_schema_name("ComplexModel"))) .build(); assert_json_eq!( json_value, @@ -1105,14 +1106,14 @@ mod tests { ObjectBuilder::new().property( "id", PropertyBuilder::new() - .component_type(ComponentType::Integer) - .format(Some(ComponentFormat::Int32)) + .schema_type(SchemaType::Integer) + .format(Some(SchemaFormat::Int32)) .description(Some("Id of credential")) .default(Some(json!(1i32))), ), ); - assert!(matches!(array.component_type, ComponentType::Array)); + assert!(matches!(array.schema_type, SchemaType::Array)); } #[test] @@ -1122,15 +1123,15 @@ mod tests { ObjectBuilder::new().property( "id", PropertyBuilder::new() - .component_type(ComponentType::Integer) - .format(Some(ComponentFormat::Int32)) + .schema_type(SchemaType::Integer) + .format(Some(SchemaFormat::Int32)) .description(Some("Id of credential")) .default(Some(json!(1i32))), ), ) .build(); - assert!(matches!(array.component_type, ComponentType::Array)); + assert!(matches!(array.schema_type, SchemaType::Array)); } #[test] @@ -1138,12 +1139,14 @@ mod tests { let components = ComponentsBuilder::new() .schemas_from_iter(vec![( "Comp", - ObjectBuilder::new() - .property( - "name", - PropertyBuilder::new().component_type(ComponentType::String), - ) - .required("name"), + Schema::from( + ObjectBuilder::new() + .property( + "name", + PropertyBuilder::new().schema_type(SchemaType::String), + ) + .required("name"), + ), )]) .responses_from_iter(vec![( "200", @@ -1168,7 +1171,7 @@ mod tests { let prop = ObjectBuilder::new() .property( "name", - PropertyBuilder::new().component_type(ComponentType::String), + PropertyBuilder::new().schema_type(SchemaType::String), ) .required("name") .build(); @@ -1186,7 +1189,7 @@ mod tests { #[test] fn reserialize_deserialized_property() { let prop = PropertyBuilder::new() - .component_type(ComponentType::String) + .schema_type(SchemaType::String) .build(); let serialized_components = serde_json::to_string(&prop).unwrap(); diff --git a/tests/openapi_derive.rs b/tests/openapi_derive.rs index fe6a216c..b8f0819f 100644 --- a/tests/openapi_derive.rs +++ b/tests/openapi_derive.rs @@ -87,9 +87,9 @@ fn derive_openapi_with_external_docs_only_url() { #[test] fn derive_openapi_with_components_in_different_module() { mod custom { - use utoipa::Component; + use utoipa::ToSchema; - #[derive(Component)] + #[derive(ToSchema)] #[allow(unused)] pub(super) struct Todo { name: String, @@ -97,7 +97,7 @@ fn derive_openapi_with_components_in_different_module() { } #[derive(OpenApi)] - #[openapi(components(custom::Todo))] + #[openapi(components(schemas(custom::Todo)))] struct ApiDoc; let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap(); @@ -125,7 +125,7 @@ fn derive_openapi_with_responses() { } #[derive(OpenApi)] - #[openapi(responses(MyResponse))] + #[openapi(components(responses(MyResponse)))] struct ApiDoc; let doc = serde_json::to_value(&ApiDoc::openapi()).unwrap(); diff --git a/tests/path_derive.rs b/tests/path_derive.rs index 76adeeb6..cade0858 100644 --- a/tests/path_derive.rs +++ b/tests/path_derive.rs @@ -7,7 +7,7 @@ use serde_json::{json, Value}; use std::collections::HashMap; use utoipa::{ openapi::{Response, ResponseBuilder, ResponsesBuilder}, - Component, IntoParams, IntoResponses, OpenApi, + IntoParams, IntoResponses, OpenApi, ToSchema, }; mod common; @@ -247,7 +247,7 @@ fn derive_path_with_security_requirements() { #[test] fn derive_path_with_parameter_schema() { - #[derive(serde::Deserialize, utoipa::Component)] + #[derive(serde::Deserialize, utoipa::ToSchema)] struct Since { /// Some date #[allow(dead_code)] @@ -314,7 +314,7 @@ fn derive_path_with_parameter_schema() { #[test] fn derive_path_with_parameter_inline_schema() { - #[derive(serde::Deserialize, utoipa::Component)] + #[derive(serde::Deserialize, utoipa::ToSchema)] struct Since { /// Some date #[allow(dead_code)] @@ -395,7 +395,7 @@ fn derive_path_with_parameter_inline_schema() { #[test] fn derive_path_params_map() { - #[derive(serde::Deserialize, Component)] + #[derive(serde::Deserialize, ToSchema)] enum Foo { Bar, Baz, @@ -467,8 +467,8 @@ fn derive_path_params_map() { #[test] fn derive_path_params_intoparams() { - #[derive(serde::Deserialize, Component)] - #[component(default = "foo1", example = "foo1")] + #[derive(serde::Deserialize, ToSchema)] + #[schema(default = "foo1", example = "foo1")] #[serde(rename_all = "snake_case")] enum Foo { Foo1, @@ -629,7 +629,7 @@ fn derive_path_params_intoparams() { fn derive_path_params_into_params_with_value_type() { use utoipa::OpenApi; - #[derive(Component)] + #[derive(ToSchema)] struct Foo { #[allow(unused)] value: String, diff --git a/tests/path_derive_actix.rs b/tests/path_derive_actix.rs index 38cd3291..cc1b24fa 100644 --- a/tests/path_derive_actix.rs +++ b/tests/path_derive_actix.rs @@ -7,9 +7,9 @@ use serde_json::Value; use utoipa::{ openapi::{ path::{Parameter, ParameterBuilder, ParameterIn}, - Array, ComponentFormat, PropertyBuilder, + Array, PropertyBuilder, SchemaFormat, }, - Component, IntoParams, OpenApi, + IntoParams, OpenApi, ToSchema, }; mod common; @@ -329,8 +329,7 @@ fn path_with_struct_variables_with_into_params() { ParameterBuilder::new() .name("name") .schema(Some( - PropertyBuilder::new() - .component_type(utoipa::openapi::ComponentType::String), + PropertyBuilder::new().schema_type(utoipa::openapi::SchemaType::String), )) .parameter_in(ParameterIn::Path) .build(), @@ -338,8 +337,8 @@ fn path_with_struct_variables_with_into_params() { .name("id") .schema(Some( PropertyBuilder::new() - .component_type(utoipa::openapi::ComponentType::Integer) - .format(Some(ComponentFormat::Int64)), + .schema_type(utoipa::openapi::SchemaType::Integer) + .format(Some(SchemaFormat::Int64)), )) .parameter_in(ParameterIn::Path) .build(), @@ -360,7 +359,7 @@ fn path_with_struct_variables_with_into_params() { vec![ParameterBuilder::new() .name("age") .schema(Some(Array::new( - PropertyBuilder::new().component_type(utoipa::openapi::ComponentType::String), + PropertyBuilder::new().schema_type(utoipa::openapi::SchemaType::String), ))) .parameter_in(ParameterIn::Query) .build()] @@ -492,7 +491,7 @@ fn derive_path_with_multiple_instances_same_path_params() { use actix_web::{delete, get, HttpResponse, Responder}; use serde_json::json; - #[derive(Deserialize, Serialize, Component, IntoParams)] + #[derive(Deserialize, Serialize, ToSchema, IntoParams)] #[into_params(names("id"))] struct Id(u64); @@ -619,7 +618,7 @@ fn derive_into_params_with_custom_attributes() { sort: Sort, } - #[derive(Deserialize, Component)] + #[derive(Deserialize, ToSchema)] enum Sort { Asc, Desc, @@ -641,7 +640,7 @@ fn derive_into_params_with_custom_attributes() { } #[derive(OpenApi, Default)] - #[openapi(handlers(get_foo), components(Sort))] + #[openapi(handlers(get_foo), components(schemas(Sort)))] struct ApiDoc; let doc = serde_json::to_value(ApiDoc::openapi()).unwrap(); diff --git a/tests/path_response_derive_test.rs b/tests/path_response_derive_test.rs index 6f6b6496..2e889b4f 100644 --- a/tests/path_response_derive_test.rs +++ b/tests/path_response_derive_test.rs @@ -9,7 +9,7 @@ macro_rules! test_fn { #[allow(unused)] mod $name { #[allow(unused)] - #[derive(utoipa::Component)] + #[derive(utoipa::ToSchema)] struct Foo { name: String, } diff --git a/tests/request_body_derive_test.rs b/tests/request_body_derive_test.rs index fbaaa52e..e12a8b17 100644 --- a/tests/request_body_derive_test.rs +++ b/tests/request_body_derive_test.rs @@ -9,7 +9,7 @@ macro_rules! test_fn { ( module: $name:ident, body: $($body:tt)* ) => { #[allow(unused)] mod $name { - #[derive(utoipa::Component)] + #[derive(utoipa::ToSchema)] /// Some struct pub struct Foo { /// Some name @@ -386,7 +386,7 @@ fn derive_request_body_primitive_ref_path_success() { #[derive(OpenApi, Default)] #[openapi( handlers(derive_request_body_primitive_ref_path::post_foo), - components(derive_request_body_primitive_ref_path::Foo as path::to::Foo) + components(schemas(derive_request_body_primitive_ref_path::Foo as path::to::Foo)) )] struct ApiDoc; diff --git a/tests/component_derive_no_serde_json.rs b/tests/schema_derive_no_serde_json.rs similarity index 62% rename from tests/component_derive_no_serde_json.rs rename to tests/schema_derive_no_serde_json.rs index 6ee4f3f5..78a6a238 100644 --- a/tests/component_derive_no_serde_json.rs +++ b/tests/schema_derive_no_serde_json.rs @@ -2,12 +2,12 @@ use std::{print, println}; -use utoipa::Component; +use utoipa::ToSchema; #[test] fn derive_component_with_string_example_compiles_success() { - #[derive(Component)] - #[component(example = r#"{"foo": "bar"}"#)] + #[derive(ToSchema)] + #[schema(example = r#"{"foo": "bar"}"#)] struct Foo { bar: String, } @@ -15,9 +15,9 @@ fn derive_component_with_string_example_compiles_success() { #[test] fn derive_component_with_string_example_attributes_compiles_success() { - #[derive(Component)] + #[derive(ToSchema)] struct Foo { - #[component(example = r#""bar""#, default = r#""foobar""#)] + #[schema(example = r#""bar""#, default = r#""foobar""#)] bar: String, } } diff --git a/tests/component_derive_test.rs b/tests/schema_derive_test.rs similarity index 95% rename from tests/component_derive_test.rs rename to tests/schema_derive_test.rs index 23f61fb8..47e5ec30 100644 --- a/tests/component_derive_test.rs +++ b/tests/schema_derive_test.rs @@ -7,14 +7,14 @@ use chrono::{Date, DateTime, Duration, Utc}; use serde::Serialize; use serde_json::{json, Value}; -use utoipa::{Component, OpenApi}; +use utoipa::{OpenApi, ToSchema}; mod common; macro_rules! api_doc { ( $( #[$attr:meta] )* $key:ident $name:ident $body:tt ) => {{ #[allow(dead_code)] - #[derive(Component)] + #[derive(ToSchema)] $(#[$attr])* $key $name $body @@ -23,7 +23,7 @@ macro_rules! api_doc { ( $( #[$attr:meta] )* $key:ident $name:ident $body:tt; ) => {{ #[allow(dead_code)] - #[derive(Component)] + #[derive(ToSchema)] $(#[$attr])* $key $name $body; @@ -32,7 +32,7 @@ macro_rules! api_doc { ( $( #[$attr:meta] )* $key:ident $name:ident< $($life:lifetime)? $($generic:ident)? > $body:tt ) => {{ #[allow(dead_code)] - #[derive(Component)] + #[derive(ToSchema)] $(#[$attr])* $key $name<$($life)? $($generic)?> $body @@ -41,7 +41,7 @@ macro_rules! api_doc { ( @doc $name:ident $( $generic:tt )* ) => {{ #[derive(OpenApi)] - #[openapi(components($name$($generic)*))] + #[openapi(components(schemas($name$($generic)*)))] struct ApiDoc; let json = serde_json::to_value(ApiDoc::openapi()).unwrap(); @@ -67,7 +67,7 @@ fn derive_map_type() { #[test] fn derive_map_ref() { - #[derive(Component)] + #[derive(ToSchema)] enum Foo {} let map = api_doc! { @@ -84,7 +84,7 @@ fn derive_map_ref() { #[test] fn derive_enum_with_additional_properties_success() { let mode = api_doc! { - #[component(default = "Mode1", example = "Mode2")] + #[schema(default = "Mode1", example = "Mode2")] enum Mode { Mode1, Mode2 } @@ -120,7 +120,7 @@ fn derive_enum_with_defaults_success() { #[test] fn derive_enum_with_with_custom_default_fn_success() { let mode = api_doc! { - #[component(default = mode_custom_default_fn)] + #[schema(default = mode_custom_default_fn)] enum Mode { Mode1, Mode2 @@ -162,9 +162,9 @@ fn derive_struct_with_defaults_success() { fn derive_struct_with_custom_properties_success() { let book = api_doc! { struct Book { - #[component(default = String::default)] + #[schema(default = String::default)] name: String, - #[component( + #[schema( default = "testhash", example = "base64 text", format = Byte, @@ -190,7 +190,7 @@ fn derive_struct_with_optional_properties_success() { struct Book; let owner = api_doc! { struct Owner { - #[component(default = 1)] + #[schema(default = 1)] id: u64, enabled: Option, books: Option>, @@ -379,7 +379,7 @@ fn derive_struct_nested_vec_success() { #[test] fn derive_struct_with_example() { let pet = api_doc! { - #[component(example = json!({"name": "bob the cat", "age": 8}))] + #[schema(example = json!({"name": "bob the cat", "age": 8}))] struct Pet { name: String, age: i32 @@ -419,7 +419,7 @@ fn derive_unnamed_struct_deprecated_success() { #[allow(deprecated)] let pet_age = api_doc! { #[deprecated] - #[component(example = 8)] + #[schema(example = 8)] struct PetAge(u64); }; @@ -432,7 +432,7 @@ fn derive_unnamed_struct_deprecated_success() { #[test] fn derive_unnamed_struct_example_json_array_success() { let pet_age = api_doc! { - #[component(example = "0", default = u64::default)] + #[schema(example = "0", default = u64::default)] struct PetAge(u64, u64); }; @@ -537,7 +537,7 @@ fn derive_with_box_and_refcell() { #[test] fn derive_struct_with_inline() { - #[derive(utoipa::Component)] + #[derive(utoipa::ToSchema)] #[allow(unused)] struct Foo { name: &'static str, @@ -545,13 +545,13 @@ fn derive_struct_with_inline() { let greeting = api_doc! { struct Greeting { - #[component(inline)] + #[schema(inline)] foo1: Foo, - #[component(inline)] + #[schema(inline)] foo2: Option, - #[component(inline)] + #[schema(inline)] foo3: Option>, - #[component(inline)] + #[schema(inline)] foo4: Vec, } }; @@ -807,13 +807,13 @@ fn derive_complex_enum_title() { let value: Value = api_doc! { #[derive(Serialize)] enum Bar { - #[component(title = "Unit")] + #[schema(title = "Unit")] UnitValue, - #[component(title = "Named")] + #[schema(title = "Named")] NamedFields { id: &'static str, }, - #[component(title = "Unnamed")] + #[schema(title = "Unnamed")] UnnamedFields(Foo), } }; @@ -1065,9 +1065,9 @@ fn derive_complex_enum_serde_tag_title() { #[derive(Serialize)] #[serde(tag = "tag")] enum Bar { - #[component(title = "Unit")] + #[schema(title = "Unit")] UnitValue, - #[component(title = "Named")] + #[schema(title = "Named")] NamedFields { id: &'static str, }, @@ -1121,9 +1121,9 @@ fn derive_complex_enum_serde_tag_title() { fn derive_struct_with_read_only_and_write_only() { let user = api_doc! { struct User { - #[component(read_only)] + #[schema(read_only)] username: String, - #[component(write_only)] + #[schema(write_only)] password: String } }; @@ -1141,15 +1141,15 @@ fn derive_struct_with_read_only_and_write_only() { #[test] fn derive_struct_xml() { let user = api_doc! { - #[component(xml(name = "user", prefix = "u", namespace = "https://mynamespace.test"))] + #[schema(xml(name = "user", prefix = "u", namespace = "https://mynamespace.test"))] struct User { - #[component(xml(attribute, prefix = "u"))] + #[schema(xml(attribute, prefix = "u"))] id: i64, - #[component(xml(name = "user_name", prefix = "u"))] + #[schema(xml(name = "user_name", prefix = "u"))] username: String, - #[component(xml(wrapped(name = "linkList"), name = "link"))] + #[schema(xml(wrapped(name = "linkList"), name = "link"))] links: Vec, - #[component(xml(wrapped, name = "photo_url"))] + #[schema(xml(wrapped, name = "photo_url"))] photos_urls: Vec } }; @@ -1294,7 +1294,7 @@ fn derive_struct_component_field_type_override() { let post = api_doc! { struct Post { id: i32, - #[component(value_type = String)] + #[schema(value_type = String)] value: i64, } }; @@ -1312,7 +1312,7 @@ fn derive_struct_component_field_type_path_override() { let post = api_doc! { struct Post { id: i32, - #[component(value_type = path::to::Foo)] + #[schema(value_type = path::to::Foo)] value: i64, } }; @@ -1330,7 +1330,7 @@ fn derive_struct_component_field_type_override_with_format() { let post = api_doc! { struct Post { id: i32, - #[component(value_type = String, format = Byte)] + #[schema(value_type = String, format = Byte)] value: i64, } }; @@ -1348,7 +1348,7 @@ fn derive_struct_component_field_type_override_with_format_with_vec() { let post = api_doc! { struct Post { id: i32, - #[component(value_type = String, format = Binary)] + #[schema(value_type = String, format = Binary)] value: Vec, } }; @@ -1362,9 +1362,9 @@ fn derive_struct_component_field_type_override_with_format_with_vec() { } #[test] -fn derive_unnamed_struct_component_type_override() { +fn derive_unnamed_struct_schema_type_override() { let value = api_doc! { - #[component(value_type = String)] + #[schema(value_type = String)] struct Value(i64); }; @@ -1375,9 +1375,9 @@ fn derive_unnamed_struct_component_type_override() { } #[test] -fn derive_unnamed_struct_component_type_override_with_format() { +fn derive_unnamed_struct_schema_type_override_with_format() { let value = api_doc! { - #[component(value_type = String, format = Byte)] + #[schema(value_type = String, format = Byte)] struct Value(i64); }; @@ -1391,7 +1391,7 @@ fn derive_unnamed_struct_component_type_override_with_format() { fn derive_struct_override_type_with_any_type() { let value = api_doc! { struct Value { - #[component(value_type = Any)] + #[schema(value_type = Any)] field: String, } }; @@ -1419,7 +1419,7 @@ fn derive_struct_override_type_with_a_reference() { let value = api_doc! { struct Value { - #[component(value_type = NewBar)] + #[schema(value_type = NewBar)] field: String, } }; @@ -1466,7 +1466,7 @@ fn derive_struct_with_rust_decimal_with_type_override() { let post = api_doc! { struct Post { id: i32, - #[component(value_type = f64)] + #[schema(value_type = f64)] rating: Decimal, } }; @@ -1599,10 +1599,10 @@ fn derive_component_with_aliases() { struct A; #[derive(Debug, OpenApi)] - #[openapi(components(MyAlias))] + #[openapi(components(schemas(MyAlias)))] struct ApiDoc; - #[derive(Component)] + #[derive(ToSchema)] #[aliases(MyAlias = Bar)] struct Bar { #[allow(dead_code)] @@ -1620,7 +1620,7 @@ fn derive_component_with_aliases() { #[test] fn derive_component_with_into_params_value_type() { - #[derive(Component)] + #[derive(ToSchema)] struct Foo { #[allow(unused)] value: String, @@ -1629,21 +1629,21 @@ fn derive_component_with_into_params_value_type() { let doc = api_doc! { #[allow(unused)] struct Random { - #[component(value_type = i64)] + #[schema(value_type = i64)] id: String, - #[component(value_type = Any)] + #[schema(value_type = Any)] another_id: String, - #[component(value_type = Vec>)] + #[schema(value_type = Vec>)] value1: Vec, - #[component(value_type = Vec)] + #[schema(value_type = Vec)] value2: Vec, - #[component(value_type = Option)] + #[schema(value_type = Option)] value3: i64, - #[component(value_type = Option)] + #[schema(value_type = Option)] value4: i64, - #[component(value_type = Vec)] + #[schema(value_type = Vec)] value5: i64, - #[component(value_type = Vec)] + #[schema(value_type = Vec)] value6: i64, } }; @@ -1708,7 +1708,7 @@ fn derive_component_with_into_params_value_type() { #[test] fn derive_component_with_complex_enum_lifetimes() { - #[derive(Component)] + #[derive(ToSchema)] struct Foo<'foo> { #[allow(unused)] field: &'foo str, diff --git a/tests/utoipa_gen_test.rs b/tests/utoipa_gen_test.rs index 25deeb5a..3734f7d8 100644 --- a/tests/utoipa_gen_test.rs +++ b/tests/utoipa_gen_test.rs @@ -9,29 +9,29 @@ use utoipa::{ security::{HttpAuthScheme, HttpBuilder, SecurityScheme}, server::{ServerBuilder, ServerVariableBuilder}, }, - Component, Modify, OpenApi, + Modify, OpenApi, ToSchema, }; -#[derive(Deserialize, Serialize, Component)] -#[component(example = json!({"name": "bob the cat", "id": 1}))] +#[derive(Deserialize, Serialize, ToSchema)] +#[schema(example = json!({"name": "bob the cat", "id": 1}))] struct Pet { id: u64, name: String, age: Option, } -// #[derive(Component)] +// #[derive(ToSchema)] // struct Status { // status: StatusType, // } -// #[derive(Component)] +// #[derive(ToSchema)] // enum StatusType { // Ok, // NotOk, // } -// #[derive(Component)] +// #[derive(ToSchema)] // enum Random { // Response { id: String }, // PetResponse(Pet), @@ -39,7 +39,7 @@ struct Pet { // UnitValue, // } -// #[derive(Serialize, Deserialize, Component)] +// #[derive(Serialize, Deserialize, ToSchema)] // struct Simple { // greeting: &'static str, // cow: Cow<'static, str>, @@ -80,7 +80,7 @@ mod pet_api { #[derive(Default, OpenApi)] #[openapi( handlers(pet_api::get_pet_by_id), - components(Pet, GenericC, GenericD), + components(schemas(Pet, GenericC, GenericD)), modifiers(&Foo), security( (), @@ -92,7 +92,7 @@ struct ApiDoc; macro_rules! build_foo { ($typ: ident, $d: ty, $r: ty) => { - #[derive(Debug, Serialize, Component)] + #[derive(Debug, Serialize, ToSchema)] struct $typ { data: $d, resources: $r, @@ -100,17 +100,17 @@ macro_rules! build_foo { }; } -#[derive(Deserialize, Serialize, Component)] +#[derive(Deserialize, Serialize, ToSchema)] struct A { a: String, } -#[derive(Deserialize, Serialize, Component)] +#[derive(Deserialize, Serialize, ToSchema)] struct B { b: i64, } -#[derive(Deserialize, Serialize, Component)] +#[derive(Deserialize, Serialize, ToSchema)] #[aliases(GenericC = C, GenericD = C)] struct C { field_1: R, diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs index ef4a8aec..64349376 100644 --- a/utoipa-gen/src/lib.rs +++ b/utoipa-gen/src/lib.rs @@ -10,7 +10,7 @@ use std::{borrow::Cow, mem, ops::Deref}; use doc_comment::CommentAttributes; -use schema::component::Component; +use schema::schema::Schema; use ext::{PathOperationResolver, PathOperations, PathResolver}; use openapi::OpenApi; @@ -28,12 +28,12 @@ use syn::{ PathArguments, PathSegment, Token, TypePath, }; -mod component_type; mod doc_comment; mod ext; mod openapi; mod path; mod schema; +mod schema_type; mod security_requirement; use crate::path::{Path, PathAttr}; @@ -46,10 +46,11 @@ use crate::path::{Path, PathAttr}; use ext::ArgumentResolver; #[proc_macro_error] -#[proc_macro_derive(Component, attributes(component, aliases))] -/// Component derive macro. +#[proc_macro_derive(ToSchema, attributes(schema, aliases))] +/// ToSchema derive macro. /// -/// This is `#[derive]` implementation for [`Component`][c] trait. The macro accepts one `component` +/// This is `#[derive]` implementation for [`ToSchema`][to_schema] trait. The macro accepts one +/// `schema` /// attribute optionally which can be used to enhance generated documentation. The attribute can be placed /// at item level or field level in struct and enums. Currently placing this attribute to unnamed field does /// not have any effect. @@ -61,40 +62,40 @@ use ext::ArgumentResolver; /// OpenAPI. OpenAPI has only a boolean flag to determine deprecation. While it is totally okay to declare deprecated with reason /// `#[deprecated = "There is better way to do this"]` the reason would not render in OpenAPI spec. /// -/// # Struct Optional Configuration Options for `#[component(...)]` +/// # Struct Optional Configuration Options for `#[schema(...)]` /// * `example = ...` Can be either _`json!(...)`_ or literal string that can be parsed to json. _`json!`_ /// should be something that _`serde_json::json!`_ can parse as a _`serde_json::Value`_. [^json] /// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to Structs. /// /// [^json]: **json** feature need to be enabled for _`json!(...)`_ type to work. /// -/// # Enum Optional Configuration Options for `#[component(...)]` +/// # Enum Optional Configuration Options for `#[schema(...)]` /// * `example = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] /// * `default = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] /// -/// # Unnamed Field Struct Optional Configuration Options for `#[component(...)]` +/// # Unnamed Field Struct Optional Configuration Options for `#[schema(...)]` /// * `example = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] /// * `default = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] -/// * `format = ...` Any variant of a [`ComponentFormat`][format] to use for the property. By default the format is derived from +/// * `format = ...` Any variant of a [`SchemaFormat`][format] to use for the property. By default the format is derived from /// the type of the property according OpenApi spec. /// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec. /// This is useful in cases where the default type does not correspond to the actual type e.g. when -/// any third-party types are used which are not [`Component`][c]s nor [`primitive` types][primitive]. +/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive]. /// Value can be any Rust type what normally could be used to serialize to JSON or custom type such as _`Any`_. /// -/// # Named Fields Optional Configuration Options for `#[component(...)]` +/// # Named Fields Optional Configuration Options for `#[schema(...)]` /// * `example = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] /// * `default = ...` Can be literal value, method reference or _`json!(...)`_. [^json2] -/// * `format = ...` Any variant of a [`ComponentFormat`][format] to use for the property. By default the format is derived from +/// * `format = ...` Any variant of a [`SchemaFormat`][format] to use for the property. By default the format is derived from /// the type of the property according OpenApi spec. /// * `write_only` Defines property is only used in **write** operations *POST,PUT,PATCH* but not in *GET* /// * `read_only` Defines property is only used in **read** operations *GET* but not in *POST,PUT,PATCH* /// * `xml(...)` Can be used to define [`Xml`][xml] object properties applicable to named fields. /// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec. /// This is useful in cases where the default type does not correspond to the actual type e.g. when -/// any third-party types are used which are not [`Component`][c]s nor [`primitive` types][primitive]. +/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive]. /// Value can be any Rust type what normally could be used to serialize to JSON or custom type such as _`Any`_. -/// * `inline` If the type of this field implements [`Component`][c], then the schema definition +/// * `inline` If the type of this field implements [`ToSchema`][to_schema], then the schema definition /// will be inlined. **warning:** Don't use this for recursive data types! /// /// [^json2]: Values are converted to string if **json** feature is not enabled. @@ -112,7 +113,7 @@ use ext::ArgumentResolver; /// /// # Partial `#[serde(...)]` attributes support /// -/// Component derive has partial support for [serde attributes](https://serde.rs/attributes.html). These supported attributes will reflect to the +/// ToSchema derive has partial support for [serde attributes](https://serde.rs/attributes.html). These supported attributes will reflect to the /// generated OpenAPI doc. For example if _`#[serde(skip)]`_ is defined the attribute will not show up in the OpenAPI spec at all since it will not never /// be serialized anyway. Similarly the _`rename`_ and _`rename_all`_ will reflect to the generated OpenAPI doc. /// @@ -129,11 +130,11 @@ use ext::ArgumentResolver; /// /// ```rust /// # use serde::Serialize; -/// # use utoipa::Component; -/// #[derive(Serialize, Component)] +/// # use utoipa::ToSchema; +/// #[derive(Serialize, ToSchema)] /// struct Foo(String); /// -/// #[derive(Serialize, Component)] +/// #[derive(Serialize, ToSchema)] /// #[serde(rename_all = "camelCase")] /// enum Bar { /// UnitValue, @@ -152,11 +153,11 @@ use ext::ArgumentResolver; /// Add custom `tag` to change JSON representation to be internally tagged. /// ```rust /// # use serde::Serialize; -/// # use utoipa::Component; -/// #[derive(Serialize, Component)] +/// # use utoipa::ToSchema; +/// #[derive(Serialize, ToSchema)] /// struct Foo(String); /// -/// #[derive(Serialize, Component)] +/// #[derive(Serialize, ToSchema)] /// #[serde(tag = "tag")] /// enum Bar { /// UnitValue, @@ -167,17 +168,17 @@ use ext::ArgumentResolver; /// } /// ``` /// -/// # Generic components with aliases +/// # Generic schemas with aliases /// -/// Components can also be generic which allows reusing types. This enables certain behaviour patters +/// Schemas can also be generic which allows reusing types. This enables certain behaviour patters /// where super type delcares common code for type aliases. /// /// In this example we have common `Status` type which accepts one generic type. It is then defined /// with `#[aliases(...)]` that it is going to be used with [`std::string::String`] and [`i32`] values. -/// The generic argument could also be another [`Component`][c] as well. +/// The generic argument could also be another [`ToSchema`][to_schema] as well. /// ```rust -/// # use utoipa::{Component, OpenApi}; -/// #[derive(Component)] +/// # use utoipa::{ToSchema, OpenApi}; +/// #[derive(ToSchema)] /// #[aliases(StatusMessage = Status, StatusNumber = Status)] /// struct Status { /// value: T @@ -185,7 +186,7 @@ use ext::ArgumentResolver; /// /// #[derive(OpenApi)] /// #[openapi( -/// components(StatusMessage, StatusNumber) +/// components(schemas(StatusMessage, StatusNumber)) /// )] /// struct ApiDoc; /// ``` @@ -200,9 +201,9 @@ use ext::ArgumentResolver; /// /// Example struct with struct level example. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] -/// #[component(example = json!({"name": "bob the cat", "id": 0}))] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] +/// #[schema(example = json!({"name": "bob the cat", "id": 0}))] /// struct Pet { /// id: u64, /// name: String, @@ -210,12 +211,12 @@ use ext::ArgumentResolver; /// } /// ``` /// -/// The `component` attribute can also be placed at field level as follows. +/// The `schema` attribute can also be placed at field level as follows. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// struct Pet { -/// #[component(example = 1, default = 0)] +/// #[schema(example = 1, default = 0)] /// id: u64, /// name: String, /// age: Option, @@ -224,12 +225,12 @@ use ext::ArgumentResolver; /// /// You can also use method reference for attribute values. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// struct Pet { -/// #[component(example = u64::default, default = u64::default)] +/// #[schema(example = u64::default, default = u64::default)] /// id: u64, -/// #[component(default = default_name)] +/// #[schema(default = default_name)] /// name: String, /// age: Option, /// } @@ -239,11 +240,11 @@ use ext::ArgumentResolver; /// } /// ``` /// -/// For enums and unnamed field structs you can define `component` at type level. +/// For enums and unnamed field structs you can define `schema` at type level. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] -/// #[component(example = "Bus")] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] +/// #[schema(example = "Bus")] /// enum VehicleType { /// Rocket, Car, Bus, Submarine /// } @@ -251,14 +252,14 @@ use ext::ArgumentResolver; /// /// Also you write complex enum combining all above types. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// enum ErrorResponse { /// InvalidCredentials, -/// #[component(default = String::default, example = "Pet not found")] +/// #[schema(default = String::default, example = "Pet not found")] /// NotFound(String), /// System { -/// #[component(example = "Unknown system failure")] +/// #[schema(example = "Unknown system failure")] /// details: String, /// } /// } @@ -266,37 +267,37 @@ use ext::ArgumentResolver; /// /// It is possible to specify the title of each variant to help generators create named structures. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// enum ErrorResponse { -/// #[component(title = "InvalidCredentials")] +/// #[schema(title = "InvalidCredentials")] /// InvalidCredentials, -/// #[component(title = "NotFound")] +/// #[schema(title = "NotFound")] /// NotFound(String), /// } /// ``` /// /// Use `xml` attribute to manipulate xml output. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] -/// #[component(xml(name = "user", prefix = "u", namespace = "https://user.xml.schema.test"))] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] +/// #[schema(xml(name = "user", prefix = "u", namespace = "https://user.xml.schema.test"))] /// struct User { -/// #[component(xml(attribute, prefix = "u"))] +/// #[schema(xml(attribute, prefix = "u"))] /// id: i64, -/// #[component(xml(name = "user_name", prefix = "u"))] +/// #[schema(xml(name = "user_name", prefix = "u"))] /// username: String, -/// #[component(xml(wrapped(name = "linkList"), name = "link"))] +/// #[schema(xml(wrapped(name = "linkList"), name = "link"))] /// links: Vec, -/// #[component(xml(wrapped, name = "photo_url"))] +/// #[schema(xml(wrapped, name = "photo_url"))] /// photos_urls: Vec /// } /// ``` /// /// Use of Rust's own `#[deprecated]` attribute will reflect to generated OpenAPI spec. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// #[deprecated] /// struct User { /// id: i64, @@ -308,64 +309,64 @@ use ext::ArgumentResolver; /// ``` /// /// Enforce type being used in OpenAPI spec to [`String`] with `value_type` and set format to octet stream -/// with [`ComponentFormat::Binary`][binary]. +/// with [`SchemaFormat::Binary`][binary]. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] /// struct Post { /// id: i32, -/// #[component(value_type = String, format = Binary)] +/// #[schema(value_type = String, format = Binary)] /// value: Vec, /// } /// ``` /// /// Enforce type being used in OpenAPI spec to [`String`] with `value_type` option. /// ```rust -/// # use utoipa::Component; -/// #[derive(Component)] -/// #[component(value_type = String)] +/// # use utoipa::ToSchema; +/// #[derive(ToSchema)] +/// #[schema(value_type = String)] /// struct Value(i64); /// ``` /// /// Override the `Bar` reference with a `custom::NewBar` reference. /// ```rust -/// # use utoipa::Component; +/// # use utoipa::ToSchema; /// # mod custom { /// # struct NewBar; /// # } /// # /// # struct Bar; -/// #[derive(Component)] +/// #[derive(ToSchema)] /// struct Value { -/// #[component(value_type = custom::NewBar)] +/// #[schema(value_type = custom::NewBar)] /// field: Bar, /// }; /// ``` /// /// Use a virtual `Any` type to render generic `object` in OpenAPI spec. /// ```rust -/// # use utoipa::Component; +/// # use utoipa::ToSchema; /// # mod custom { /// # struct NewBar; /// # } /// # /// # struct Bar; -/// #[derive(Component)] +/// #[derive(ToSchema)] /// struct Value { -/// #[component(value_type = Any)] +/// #[schema(value_type = Any)] /// field: Bar, /// }; /// ``` /// /// More examples for _`value_type`_ in [`IntoParams` derive docs][into_params]. /// -/// [c]: trait.Component.html -/// [format]: openapi/schema/enum.ComponentFormat.html -/// [binary]: openapi/schema/enum.ComponentFormat.html#variant.Binary +/// [to_schema]: trait.ToSchema.html +/// [format]: openapi/schema/enum.SchemaFormat.html +/// [binary]: openapi/schema/enum.SchemaFormat.html#variant.Binary /// [xml]: openapi/xml/struct.Xml.html /// [into_params]: derive.IntoParams.html /// [primitive]: https://doc.rust-lang.org/std/primitive/index.html -pub fn derive_component(input: TokenStream) -> TokenStream { +pub fn derive_to_schema(input: TokenStream) -> TokenStream { let DeriveInput { attrs, ident, @@ -374,9 +375,9 @@ pub fn derive_component(input: TokenStream) -> TokenStream { vis, } = syn::parse_macro_input!(input); - let component = Component::new(&data, &attrs, &ident, &generics, &vis); + let schema = Schema::new(&data, &attrs, &ident, &generics, &vis); - component.to_token_stream().into() + schema.to_token_stream().into() } #[proc_macro_error] @@ -416,8 +417,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { /// # Request Body Attributes /// /// * `content = ...` Can be used to define the content object. Should be an identifier, slice or option -/// E.g. _`Pet`_ or _`[Pet]`_ or _`Option`_. Where the type implments [`Component`][component], -/// it can also be wrapped in `inline(...)` in order to inline the component schema definition. +/// E.g. _`Pet`_ or _`[Pet]`_ or _`Option`_. Where the type implments [`ToSchema`][to_schema], +/// it can also be wrapped in `inline(...)` in order to inline the schema definition. /// E.g. _`inline(Pet)`_. /// * `description = "..."` Define the description for the request body object as str. /// * `content_type = "..."` Can be used to override the default behavior of auto resolving the content type @@ -442,8 +443,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { /// * `status = ...` Is valid http status code. E.g. _`200`_ /// * `description = "..."` Define description for the response as str. /// * `body = ...` Optional response body object type. When left empty response does not expect to send any -/// response body. Should be an identifier or slice. E.g _`Pet`_ or _`[Pet]`_. Where the type implments [`Component`][component], -/// it can also be wrapped in `inline(...)` in order to inline the component schema definition. E.g. _`inline(Pet)`_. +/// response body. Should be an identifier or slice. E.g _`Pet`_ or _`[Pet]`_. Where the type implments [`ToSchema`][to_schema], +/// it can also be wrapped in `inline(...)` in order to inline the schema definition. E.g. _`inline(Pet)`_. /// * `content_type = "..." | content_type = [...]` Can be used to override the default behavior of auto resolving the content type /// from the `body` attribute. If defined the value should be valid content type such as /// _`application/json`_. By default the content type is _`text/plain`_ for @@ -515,8 +516,8 @@ pub fn derive_component(input: TokenStream) -> TokenStream { /// /// * `name` _**Must be the first argument**_. Define the name for parameter. /// * `parameter_type` Define possible type for the parameter. Type should be an identifier, slice `[Type]`, -/// option `Option`. Where the type implments [`Component`][component], it can also be wrapped in `inline(MyComponent)` -/// in order to inline the component schema definition. +/// option `Option`. Where the type implments [`ToSchema`][to_schema], it can also be wrapped in `inline(MySchema)` +/// in order to inline the schema definition. /// E.g. _`String`_ or _`[String]`_ or _`Option`_. Parameter type is placed after `name` with /// equals sign E.g. _`"id" = String`_ /// * `in` _**Must be placed after name or parameter_type**_. Define the place of the parameter. @@ -794,7 +795,7 @@ pub fn derive_component(input: TokenStream) -> TokenStream { /// /// [in_enum]: utoipa/openapi/path/enum.ParameterIn.html /// [path]: trait.Path.html -/// [component]: trait.Component.html +/// [to_schema]: trait.ToSchema.html /// [openapi]: derive.OpenApi.html /// [security]: openapi/security/struct.SecurityRequirement.html /// [security_schema]: openapi/security/struct.SecuritySchema.html @@ -879,7 +880,7 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream { /// **Accepted argument attributes:** /// /// * `handlers(...)` List of method references having attribute [`#[utoipa::path]`][path] macro. -/// * `components(...)` List of [`Component`][component]s in OpenAPI schema. +/// * `components(...)` List of [`ToSchema`][to_schema]s in OpenAPI schema. /// * `responses(...)` List of types that implement /// [`ToResponse`][to_response_trait]. /// * `modifiers(...)` List of items implementing [`Modify`][modify] trait for runtime OpenApi modification. @@ -906,15 +907,15 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// Define OpenApi schema with some paths and components. /// ```rust -/// # use utoipa::{OpenApi, Component}; +/// # use utoipa::{OpenApi, ToSchema}; /// # -/// #[derive(Component)] +/// #[derive(ToSchema)] /// struct Pet { /// name: String, /// age: i32, /// } /// -/// #[derive(Component)] +/// #[derive(ToSchema)] /// enum Status { /// Active, InActive, Locked, /// } @@ -935,7 +936,7 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream { /// #[derive(OpenApi)] /// #[openapi( /// handlers(get_pet, get_status), -/// components(Pet, Status), +/// components(schemas(Pet, Status)), /// security( /// (), /// ("my_auth" = ["read:items", "edit:items"]), @@ -952,7 +953,7 @@ pub fn path(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// [openapi]: trait.OpenApi.html /// [openapi_struct]: openapi/struct.OpenApi.html -/// [component]: derive.Component.html +/// [to_schema]: derive.ToSchema.html /// [path]: attr.path.html /// [modify]: trait.Modify.html /// [info]: openapi/info/struct.Info.html @@ -1015,11 +1016,11 @@ pub fn openapi(input: TokenStream) -> TokenStream { /// will override any example in underlying parameter type. /// * `value_type = ...` Can be used to override default type derived from type of the field used in OpenAPI spec. /// This is useful in cases where the default type does not correspond to the actual type e.g. when -/// any third-party types are used which are not [`Component`][component]s nor [`primitive` types][primitive]. +/// any third-party types are used which are not [`ToSchema`][to_schema]s nor [`primitive` types][primitive]. /// Value can be any Rust type what normally could be used to serialize to JSON or custom type such as _`Any`_. /// _`Any`_ will be rendered as generic OpenAPI object. -/// * `inline` If set, the schema for this field's type needs to be a [`Component`][component], and -/// the component schema definition will be inlined. +/// * `inline` If set, the schema for this field's type needs to be a [`ToSchema`][to_schema], and +/// the schema definition will be inlined. /// /// **Note!** `#[into_params(...)]` is only supported on unnamed struct types to declare names for the arguments. /// @@ -1080,12 +1081,12 @@ pub fn openapi(input: TokenStream) -> TokenStream { /// ``` /// /// Demonstrate [`IntoParams`][into_params] usage with the `#[into_params(...)]` container attribute to -/// be used as a path query, and inlining a component query field: +/// be used as a path query, and inlining a schema query field: /// ```rust /// use serde::Deserialize; -/// use utoipa::{IntoParams, Component}; +/// use utoipa::{IntoParams, ToSchema}; /// -/// #[derive(Deserialize, Component)] +/// #[derive(Deserialize, ToSchema)] /// #[serde(rename_all = "snake_case")] /// enum PetKind { /// Dog, @@ -1165,11 +1166,11 @@ pub fn openapi(input: TokenStream) -> TokenStream { /// } /// ``` /// -/// We can override value with another [`Component`][component]. +/// We can override value with another [`ToSchema`][to_schema]. /// ```rust -/// # use utoipa::{IntoParams, Component}; +/// # use utoipa::{IntoParams, ToSchema}; /// # -/// #[derive(Component)] +/// #[derive(ToSchema)] /// struct Id { /// value: i64, /// } @@ -1182,7 +1183,7 @@ pub fn openapi(input: TokenStream) -> TokenStream { /// } /// ``` /// -/// [component]: trait.Component.html +/// [to_schema]: trait.ToSchema.html /// [into_params]: trait.IntoParams.html /// [path_params]: attr.path.html#params-attributes /// [struct]: https://doc.rust-lang.org/std/keyword.struct.html diff --git a/utoipa-gen/src/openapi.rs b/utoipa-gen/src/openapi.rs index 8086b837..b03dd668 100644 --- a/utoipa-gen/src/openapi.rs +++ b/utoipa-gen/src/openapi.rs @@ -13,7 +13,7 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use crate::{ - parse_utils, path::PATH_STRUCT_PREFIX, schema::component, + parse_utils, path::PATH_STRUCT_PREFIX, schema::schema, security_requirement::SecurityRequirementAttr, Array, ExternalDocs, }; @@ -23,8 +23,7 @@ mod info; #[cfg_attr(feature = "debug", derive(Debug))] pub struct OpenApiAttr { handlers: Punctuated, - schemas: Punctuated, - responses: Punctuated, + components: Components, modifiers: Punctuated, security: Option>, tags: Option>, @@ -55,10 +54,7 @@ impl Parse for OpenApiAttr { openapi.handlers = parse_utils::parse_punctuated_within_parenthesis(input)?; } "components" => { - openapi.schemas = parse_utils::parse_punctuated_within_parenthesis(input)?; - } - "responses" => { - openapi.responses = parse_utils::parse_punctuated_within_parenthesis(input)?; + openapi.components = input.parse()?; } "modifiers" => { openapi.modifiers = parse_utils::parse_punctuated_within_parenthesis(input)?; @@ -93,13 +89,13 @@ impl Parse for OpenApiAttr { } #[cfg_attr(feature = "debug", derive(Debug))] -struct Component { +struct Schema { path: ExprPath, generics: Generics, alias: Option, } -impl Component { +impl Schema { fn has_lifetime_generics(&self) -> bool { self.generics .params @@ -112,7 +108,7 @@ impl Component { } } -impl Parse for Component { +impl Parse for Schema { fn parse(input: ParseStream) -> syn::Result { let path: ExprPath = input.parse()?; let generics: Generics = input.parse()?; @@ -124,7 +120,7 @@ impl Parse for Component { None }; - Ok(Component { + Ok(Schema { path, generics, alias, @@ -243,10 +239,14 @@ impl ToTokens for OpenApi { let OpenApi(attributes, ident) = self; let info = info::impl_info(); - let components = - impl_components(&attributes.schemas, &attributes.responses).map(|components| { - quote! { .components(Some(#components)) } - }); + + let components_builder_stream = attributes.components.to_token_stream(); + + let components = if !components_builder_stream.to_token_stream().is_empty() { + Some(quote! { .components(Some(#components_builder_stream)) }) + } else { + None + }; let modifiers = &attributes.modifiers; let modifiers_len = modifiers.len(); @@ -272,14 +272,15 @@ impl ToTokens for OpenApi { tokens.extend(quote! { impl utoipa::OpenApi for #ident { fn openapi() -> utoipa::openapi::OpenApi { - use utoipa::{Component, Path}; + use utoipa::{ToSchema, Path}; let mut openapi = utoipa::openapi::OpenApiBuilder::new() .info(#info) .paths(#path_items) #components #securities #tags - #external_docs.build(); + #external_docs + .build(); let _mods: [&dyn utoipa::Modify; #modifiers_len] = [#modifiers]; _mods.iter().for_each(|modifier| modifier.modify(&mut openapi)); @@ -291,12 +292,61 @@ impl ToTokens for OpenApi { } } -fn impl_components( - schemas: &Punctuated, - responses: &Punctuated, -) -> Option { - if !(schemas.is_empty() && responses.is_empty()) { - let builder_tokens = schemas.iter().fold( +#[derive(Default)] +#[cfg_attr(feature = "debug", derive(Debug))] +struct Components { + schemas: Vec, + responses: Vec, +} + +impl Parse for Components { + fn parse(input: ParseStream) -> syn::Result { + let mut content; + parenthesized!(content in input); + const EXPECTED_ATTRIBUTE: &str = + "unexpected attribute. expected one of: schemas, responses"; + + let mut schemas: Vec = Vec::new(); + let mut responses: Vec = Vec::new(); + + while !content.is_empty() { + let ident = content.parse::().map_err(|error| { + Error::new(error.span(), &format!("{}, {}", EXPECTED_ATTRIBUTE, error)) + })?; + let attribute = &*ident.to_string(); + + match attribute { + "schemas" => { + let punctuated: Punctuated = + parse_utils::parse_punctuated_within_parenthesis(&mut content)?; + let mut v: Vec = punctuated.into_iter().collect(); + schemas.append(&mut v) + } + "responses" => { + let punctuated: Punctuated = + parse_utils::parse_punctuated_within_parenthesis(&mut content)?; + let mut v: Vec = punctuated.into_iter().collect(); + responses.append(&mut v) + } + _ => return Err(syn::Error::new(ident.span(), EXPECTED_ATTRIBUTE)), + } + + if !content.is_empty() { + content.parse::()?; + } + } + + Ok(Self { schemas, responses }) + } +} + +impl ToTokens for Components { + fn to_tokens(&self, tokens: &mut TokenStream) { + if self.schemas.is_empty() && self.responses.is_empty() { + return; + } + + let builder_tokens = self.schemas.iter().fold( quote! { utoipa::openapi::ComponentsBuilder::new() }, |mut builder_tokens, component| { let path = &component.path; @@ -305,7 +355,7 @@ fn impl_components( let component_name: String = component .alias .as_ref() - .map(component::format_path_ref) + .map(schema::format_path_ref) .unwrap_or_else(|| ident.to_token_stream().to_string()); let (_, ty_generics, _) = component.generics.split_for_impl(); @@ -317,8 +367,8 @@ fn impl_components( }; builder_tokens.extend(quote_spanned! { ident.span() => - .schema(#component_name, <#path #ty_generics as utoipa::Component>::component()) - .schemas_from_iter(<#path #ty_generics as utoipa::Component>::aliases()) + .schema(#component_name, <#path #ty_generics as utoipa::ToSchema>::schema()) + .schemas_from_iter(<#path #ty_generics as utoipa::ToSchema>::aliases()) }); builder_tokens @@ -326,7 +376,7 @@ fn impl_components( ); let builder_tokens = - responses + self.responses .iter() .fold(builder_tokens, |mut builder_tokens, responses| { let path = &responses.path; @@ -337,10 +387,7 @@ fn impl_components( builder_tokens }); - let components_tokens = quote! { #builder_tokens.build() }; - Some(components_tokens) - } else { - None + tokens.extend(quote! { #builder_tokens.build() }); } } diff --git a/utoipa-gen/src/path.rs b/utoipa-gen/src/path.rs index 59eeb115..b7226b53 100644 --- a/utoipa-gen/src/path.rs +++ b/utoipa-gen/src/path.rs @@ -6,8 +6,8 @@ use quote::{format_ident, quote, ToTokens}; use syn::punctuated::Punctuated; use syn::{parenthesized, parse::Parse, Token}; -use crate::{component_type::ComponentType, security_requirement::SecurityRequirementAttr, Array}; use crate::{parse_utils, Deprecated}; +use crate::{schema_type::SchemaType, security_requirement::SecurityRequirementAttr, Array}; use self::response::Response; use self::{parameter::Parameter, request_body::RequestBodyAttr, response::Responses}; @@ -576,11 +576,11 @@ trait ContentTypeResolver { fn resolve_content_type<'a>( &self, content_type: Option<&'a String>, - component_type: &ComponentType<'a>, + schema_type: &SchemaType<'a>, ) -> &'a str { if let Some(content_type) = content_type { content_type - } else if component_type.is_primitive() { + } else if schema_type.is_primitive() { "text/plain" } else { "application/json" diff --git a/utoipa-gen/src/path/property.rs b/utoipa-gen/src/path/property.rs index 25093872..ba6c2561 100644 --- a/utoipa-gen/src/path/property.rs +++ b/utoipa-gen/src/path/property.rs @@ -2,8 +2,8 @@ use quote::{quote, quote_spanned, ToTokens}; use syn::spanned::Spanned; use crate::{ - component_type::{ComponentFormat, ComponentType}, - schema::component, + schema::schema, + schema_type::{SchemaFormat, SchemaType}, Type, }; @@ -16,23 +16,23 @@ impl<'a> Property<'a> { Self(type_definition) } - pub fn component_type(&'a self) -> ComponentType<'a> { - ComponentType(&*self.0.ty) + pub fn schema_type(&'a self) -> SchemaType<'a> { + SchemaType(&*self.0.ty) } } impl ToTokens for Property<'_> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let component_type = self.component_type(); + let schema_type = self.schema_type(); - if component_type.is_primitive() { - let mut component = quote! { - utoipa::openapi::PropertyBuilder::new().component_type(#component_type) + if schema_type.is_primitive() { + let mut schema = quote! { + utoipa::openapi::PropertyBuilder::new().schema_type(#schema_type) }; - let format: ComponentFormat = (&*component_type.0).into(); + let format: SchemaFormat = (&*schema_type.0).into(); if format.is_known_format() { - component.extend(quote! { + schema.extend(quote! { .format(Some(#format)) }) } @@ -40,32 +40,32 @@ impl ToTokens for Property<'_> { tokens.extend(if self.0.is_array { quote! { utoipa::openapi::schema::ArrayBuilder::new() - .items(#component) + .items(#schema) } } else { - component + schema }); } else { - let component_name_path = component_type.0; + let schema_name_path = schema_type.0; - let component = if self.0.is_inline { - quote_spanned! { component_name_path.span()=> - <#component_name_path as utoipa::Component>::component() + let schema = if self.0.is_inline { + quote_spanned! { schema_name_path.span()=> + <#schema_name_path as utoipa::ToSchema>::schema() } } else { - let name = component::format_path_ref(component_name_path); + let name = schema::format_path_ref(schema_name_path); quote! { - utoipa::openapi::Ref::from_component_name(#name) + utoipa::openapi::Ref::from_schema_name(#name) } }; tokens.extend(if self.0.is_array { quote! { utoipa::openapi::schema::ArrayBuilder::new() - .items(#component) + .items(#schema) } } else { - component + schema }); } } diff --git a/utoipa-gen/src/path/request_body.rs b/utoipa-gen/src/path/request_body.rs index 2f9fbbb9..bf480c47 100644 --- a/utoipa-gen/src/path/request_body.rs +++ b/utoipa-gen/src/path/request_body.rs @@ -126,7 +126,7 @@ impl ToTokens for RequestBodyAttr<'_> { let property = Property::new(body_type); let content_type = - self.resolve_content_type(self.content_type.as_ref(), &property.component_type()); + self.resolve_content_type(self.content_type.as_ref(), &property.schema_type()); let required: Required = (!body_type.is_option).into(); tokens.extend(quote! { diff --git a/utoipa-gen/src/path/response.rs b/utoipa-gen/src/path/response.rs index 61666b86..d0b7a6cb 100644 --- a/utoipa-gen/src/path/response.rs +++ b/utoipa-gen/src/path/response.rs @@ -140,7 +140,7 @@ impl ToTokens for ResponseValue<'_> { }) }) } else { - let default_type = self.resolve_content_type(None, &property.component_type()); + let default_type = self.resolve_content_type(None, &property.schema_type()); tokens.extend(quote! { .content(#default_type, #content.build()) }); diff --git a/utoipa-gen/src/schema.rs b/utoipa-gen/src/schema.rs index 6f689bd2..db2296f9 100644 --- a/utoipa-gen/src/schema.rs +++ b/utoipa-gen/src/schema.rs @@ -4,11 +4,11 @@ use proc_macro2::Ident; use proc_macro_error::{abort, abort_call_site}; use syn::{Attribute, GenericArgument, PathArguments, PathSegment, Type, TypePath}; -use crate::{component_type::ComponentType, Deprecated}; +use crate::{schema_type::SchemaType, Deprecated}; pub mod into_params; -pub mod component; +pub mod schema; /// Find `#[deprecated]` attribute from given attributes. Typically derive type attributes /// or field attributes of struct. @@ -23,7 +23,7 @@ fn get_deprecated(attributes: &[Attribute]) -> Option { } /// [`TypeTree`] of items which represents a single parsed `type` of a -/// `Component`, `Parameter` or `FnArg` +/// `Schema`, `Parameter` or `FnArg` #[cfg_attr(feature = "debug", derive(Debug, PartialEq))] pub struct TypeTree<'t> { pub path: Option>, @@ -78,16 +78,13 @@ impl<'t> TypeTree<'t> { if last_segment.arguments.is_empty() { Self::convert(Cow::Borrowed(path), last_segment) } else { - Self::resolve_component_type(Cow::Borrowed(path), last_segment) + Self::resolve_schema_type(Cow::Borrowed(path), last_segment) } }) } // Only when type is a generic type we get to this function. - fn resolve_component_type( - path: Cow<'t, TypePath>, - last_segment: &'t PathSegment, - ) -> TypeTree<'t> { + fn resolve_schema_type(path: Cow<'t, TypePath>, last_segment: &'t PathSegment) -> TypeTree<'t> { if last_segment.arguments.is_empty() { abort!( last_segment.ident, @@ -95,7 +92,7 @@ impl<'t> TypeTree<'t> { ); }; - let mut generic_component_type = Self::convert(path, last_segment); + let mut generic_schema_type = Self::convert(path, last_segment); let mut generic_types = match &last_segment.arguments { PathArguments::AngleBracketed(angle_bracketed_args) => { @@ -128,16 +125,16 @@ impl<'t> TypeTree<'t> { ), }; - generic_component_type.children = generic_types + generic_schema_type.children = generic_types .as_mut() .map(|generic_type| generic_type.map(Self::from_type).collect()); - generic_component_type + generic_schema_type } fn convert(path: Cow<'t, TypePath>, last_segment: &'t PathSegment) -> TypeTree<'t> { let generic_type = Self::get_generic_type(last_segment); - let is_primitive = ComponentType(&*path).is_primitive(); + let is_primitive = SchemaType(&*path).is_primitive(); Self { path: Some(path), diff --git a/utoipa-gen/src/schema/into_params.rs b/utoipa-gen/src/schema/into_params.rs index 624f2ba7..43824b9e 100644 --- a/utoipa-gen/src/schema/into_params.rs +++ b/utoipa-gen/src/schema/into_params.rs @@ -7,10 +7,10 @@ use syn::{ }; use crate::{ - component_type::{ComponentFormat, ComponentType}, doc_comment::CommentAttributes, parse_utils, path::parameter::{ParameterExt, ParameterIn, ParameterStyle}, + schema_type::{SchemaFormat, SchemaType}, Array, Required, }; @@ -410,7 +410,7 @@ impl ToTokens for ParamType<'_> { }; tokens.extend(quote! { - utoipa::openapi::Component::Array( + utoipa::openapi::Schema::Array( utoipa::openapi::ArrayBuilder::new().items(#param_type).build() ) }); @@ -457,13 +457,13 @@ impl ToTokens for ParamType<'_> { match component.value_type { ValueType::Primitive => { let type_path = &**component.path.as_ref().unwrap(); - let component_type = ComponentType(type_path); + let schema_type = SchemaType(type_path); tokens.extend(quote! { - utoipa::openapi::PropertyBuilder::new().component_type(#component_type) + utoipa::openapi::PropertyBuilder::new().schema_type(#schema_type) }); - let format: ComponentFormat = (type_path).into(); + let format: SchemaFormat = (type_path).into(); if format.is_known_format() { tokens.extend(quote! { .format(Some(#format)) @@ -477,7 +477,7 @@ impl ToTokens for ParamType<'_> { .expect("component should have a path"); if inline { tokens.extend(quote_spanned! {component_path.span()=> - <#component_path as utoipa::Component>::component() + <#component_path as utoipa::ToSchema>::schema() }) } else if component.is_any() { tokens.extend(quote! { @@ -492,7 +492,7 @@ impl ToTokens for ParamType<'_> { .ident .to_string(); tokens.extend(quote! { - utoipa::openapi::Ref::from_component_name(#name) + utoipa::openapi::Ref::from_schema_name(#name) }); } } diff --git a/utoipa-gen/src/schema/component.rs b/utoipa-gen/src/schema/schema.rs similarity index 83% rename from utoipa-gen/src/schema/component.rs rename to utoipa-gen/src/schema/schema.rs index 231462a8..42f89700 100644 --- a/utoipa-gen/src/schema/component.rs +++ b/utoipa-gen/src/schema/schema.rs @@ -8,13 +8,13 @@ use syn::{ }; use crate::{ - component_type::{ComponentFormat, ComponentType}, doc_comment::CommentAttributes, + schema_type::{SchemaFormat, SchemaType}, Array, Deprecated, }; use self::{ - attr::{ComponentAttr, Enum, IsInline, NamedField, Title, UnnamedFieldStruct}, + attr::{Enum, IsInline, NamedField, SchemaAttr, Title, UnnamedFieldStruct}, xml::Xml, }; @@ -26,16 +26,16 @@ use super::{ mod attr; mod xml; -pub struct Component<'a> { +pub struct Schema<'a> { ident: &'a Ident, attributes: &'a [Attribute], generics: &'a Generics, - aliases: Option>, + aliases: Option>, data: &'a Data, vis: &'a Visibility, } -impl<'a> Component<'a> { +impl<'a> Schema<'a> { pub fn new( data: &'a Data, attributes: &'a [Attribute], @@ -60,19 +60,19 @@ impl<'a> Component<'a> { } } -impl ToTokens for Component<'_> { +impl ToTokens for Schema<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let ident = self.ident; - let variant = ComponentVariant::new(self.data, self.attributes, ident, self.generics, None); + let variant = SchemaVariant::new(self.data, self.attributes, ident, self.generics, None); let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); let aliases = self.aliases.as_ref().map(|aliases| { - let alias_components = aliases + let alias_schemas = aliases .iter() .map(|alias| { let name = &*alias.name; - let variant = ComponentVariant::new( + let variant = SchemaVariant::new( self.data, self.attributes, ident, @@ -84,8 +84,8 @@ impl ToTokens for Component<'_> { .collect::>(); quote! { - fn aliases() -> Vec<(&'static str, utoipa::openapi::schema::Component)> { - #alias_components.to_vec() + fn aliases() -> Vec<(&'static str, utoipa::openapi::schema::Schema)> { + #alias_schemas.to_vec() } } }); @@ -111,8 +111,8 @@ impl ToTokens for Component<'_> { }); tokens.extend(quote! { - impl #impl_generics utoipa::Component for #ident #ty_generics #where_clause { - fn component() -> utoipa::openapi::schema::Component { + impl #impl_generics utoipa::ToSchema for #ident #ty_generics #where_clause { + fn schema() -> utoipa::openapi::schema::Schema { #variant.into() } @@ -124,32 +124,32 @@ impl ToTokens for Component<'_> { } } -enum ComponentVariant<'a> { - Named(NamedStructComponent<'a>), - Unnamed(UnnamedStructComponent<'a>), - Enum(EnumComponent<'a>), +enum SchemaVariant<'a> { + Named(NamedStructSchema<'a>), + Unnamed(UnnamedStructSchema<'a>), + Enum(EnumSchema<'a>), } -impl<'a> ComponentVariant<'a> { +impl<'a> SchemaVariant<'a> { pub fn new( data: &'a Data, attributes: &'a [Attribute], ident: &'a Ident, generics: &'a Generics, - alias: Option<&'a AliasComponent>, - ) -> ComponentVariant<'a> { + alias: Option<&'a AliasSchema>, + ) -> SchemaVariant<'a> { match data { Data::Struct(content) => match &content.fields { Fields::Unnamed(fields) => { let FieldsUnnamed { unnamed, .. } = fields; - Self::Unnamed(UnnamedStructComponent { + Self::Unnamed(UnnamedStructSchema { attributes, fields: unnamed, }) } Fields::Named(fields) => { let FieldsNamed { named, .. } = fields; - Self::Named(NamedStructComponent { + Self::Named(NamedStructSchema { attributes, fields: named, generics: Some(generics), @@ -161,7 +161,7 @@ impl<'a> ComponentVariant<'a> { "unexpected Field::Unit expected struct with Field::Named or Field::Unnamed" ), }, - Data::Enum(content) => Self::Enum(EnumComponent { + Data::Enum(content) => Self::Enum(EnumSchema { attributes, variants: &content.variants, }), @@ -173,25 +173,25 @@ impl<'a> ComponentVariant<'a> { } } -impl ToTokens for ComponentVariant<'_> { +impl ToTokens for SchemaVariant<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - Self::Enum(component) => component.to_tokens(tokens), - Self::Named(component) => component.to_tokens(tokens), - Self::Unnamed(component) => component.to_tokens(tokens), + Self::Enum(schema) => schema.to_tokens(tokens), + Self::Named(schema) => schema.to_tokens(tokens), + Self::Unnamed(schema) => schema.to_tokens(tokens), } } } #[cfg_attr(feature = "debug", derive(Debug))] -struct NamedStructComponent<'a> { +struct NamedStructSchema<'a> { fields: &'a Punctuated, attributes: &'a [Attribute], generics: Option<&'a Generics>, - alias: Option<&'a AliasComponent>, + alias: Option<&'a AliasSchema>, } -impl ToTokens for NamedStructComponent<'_> { +impl ToTokens for NamedStructSchema<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let container_rules = serde::parse_container(self.attributes); @@ -236,7 +236,7 @@ impl ToTokens for NamedStructComponent<'_> { let deprecated = super::get_deprecated(&field.attrs); let attrs = - ComponentAttr::::from_attributes_validated(&field.attrs, type_tree); + SchemaAttr::::from_attributes_validated(&field.attrs, type_tree); let override_type_tree = attrs .as_ref() @@ -247,7 +247,7 @@ impl ToTokens for NamedStructComponent<'_> { .and_then(|named_field| named_field.as_ref().xml.as_ref()); let comments = CommentAttributes::from_attributes(&field.attrs); - let component = ComponentProperty::new( + let schema_property = SchemaProperty::new( override_type_tree.as_ref().unwrap_or(type_tree), Some(&comments), attrs.as_ref(), @@ -256,10 +256,10 @@ impl ToTokens for NamedStructComponent<'_> { ); tokens.extend(quote! { - .property(#name, #component) + .property(#name, #schema_property) }); - if !component.is_option() { + if !schema_property.is_option() { tokens.extend(quote! { .required(#name) }) @@ -270,7 +270,7 @@ impl ToTokens for NamedStructComponent<'_> { tokens.extend(quote! { .deprecated(Some(#deprecated)) }); } - let attrs = ComponentAttr::::from_attributes_validated(self.attributes); + let attrs = SchemaAttr::::from_attributes_validated(self.attributes); if let Some(attrs) = attrs { tokens.extend(attrs.to_token_stream()); } @@ -284,12 +284,12 @@ impl ToTokens for NamedStructComponent<'_> { } #[cfg_attr(feature = "debug", derive(Debug))] -struct UnnamedStructComponent<'a> { +struct UnnamedStructSchema<'a> { fields: &'a Punctuated, attributes: &'a [Attribute], } -impl ToTokens for UnnamedStructComponent<'_> { +impl ToTokens for UnnamedStructSchema<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { let fields_len = self.fields.len(); let first_field = self.fields.first().unwrap(); @@ -299,16 +299,15 @@ impl ToTokens for UnnamedStructComponent<'_> { let all_fields_are_same = fields_len == 1 || self.fields.iter().skip(1).all(|field| { - let component_part = &TypeTree::from_type(&field.ty); + let schema_part = &TypeTree::from_type(&field.ty); - first_part == component_part + first_part == schema_part }); - let attrs = - attr::parse_component_attr::>(self.attributes); + let attrs = attr::parse_schema_attr::>(self.attributes); let deprecated = super::get_deprecated(self.attributes); if all_fields_are_same { - let override_component = attrs.as_ref().and_then(|unnamed_struct| { + let override_schema = attrs.as_ref().and_then(|unnamed_struct| { unnamed_struct .as_ref() .value_type @@ -316,16 +315,16 @@ impl ToTokens for UnnamedStructComponent<'_> { .map(TypeTree::from_type) }); - if override_component.is_some() { - is_object = override_component + if override_schema.is_some() { + is_object = override_schema .as_ref() .map(|override_type| matches!(override_type.value_type, ValueType::Object)) .unwrap_or_default(); } tokens.extend( - ComponentProperty::new( - override_component.as_ref().unwrap_or(first_part), + SchemaProperty::new( + override_schema.as_ref().unwrap_or(first_part), None, attrs.as_ref(), deprecated.as_ref(), @@ -368,12 +367,12 @@ impl ToTokens for UnnamedStructComponent<'_> { } #[cfg_attr(feature = "debug", derive(Debug))] -struct EnumComponent<'a> { +struct EnumSchema<'a> { variants: &'a Punctuated, attributes: &'a [Attribute], } -impl ToTokens for EnumComponent<'_> { +impl ToTokens for EnumSchema<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { if self .variants @@ -418,7 +417,7 @@ impl SimpleEnum<'_> { .property( #tag, utoipa::openapi::schema::PropertyBuilder::new() - .component_type(utoipa::openapi::ComponentType::String) + .schema_type(utoipa::openapi::SchemaType::String) .enum_values::<[&str; 1], &str>(Some([#enum_value])) ) .required(#tag) @@ -441,7 +440,7 @@ impl SimpleEnum<'_> { let len = enum_values.len(); quote! { utoipa::openapi::PropertyBuilder::new() - .component_type(utoipa::openapi::ComponentType::String) + .schema_type(utoipa::openapi::SchemaType::String) .enum_values::<[&str; #len], &str>(Some(#enum_values)) } } @@ -476,7 +475,7 @@ impl ToTokens for SimpleEnum<'_> { _ => Self::variants_tokens(enum_values), }); - let attrs = attr::parse_component_attr::>(self.attributes); + let attrs = attr::parse_schema_attr::>(self.attributes); if let Some(attributes) = attrs { tokens.extend(attributes.to_token_stream()); } @@ -501,24 +500,24 @@ struct ComplexEnum<'a> { impl ComplexEnum<'_> { fn unit_variant_tokens( variant_name: String, - variant_title: Option>, + variant_title: Option>, ) -> TokenStream { quote! { utoipa::openapi::PropertyBuilder::new() #variant_title - .component_type(utoipa::openapi::ComponentType::String) + .schema_type(utoipa::openapi::SchemaType::String) .enum_values::<[&str; 1], &str>(Some([#variant_name])) } } /// Produce tokens that represent a variant of a [`ComplexEnum`]. fn variant_tokens( variant_name: String, - variant_title: Option>, + variant_title: Option>, variant: &Variant, ) -> TokenStream { match &variant.fields { Fields::Named(named_fields) => { - let named_enum = NamedStructComponent { + let named_enum = NamedStructSchema { attributes: &variant.attrs, fields: &named_fields.named, generics: None, @@ -532,7 +531,7 @@ impl ComplexEnum<'_> { } } Fields::Unnamed(unnamed_fields) => { - let unnamed_enum = UnnamedStructComponent { + let unnamed_enum = UnnamedStructSchema { attributes: &variant.attrs, fields: &unnamed_fields.unnamed, }; @@ -552,12 +551,12 @@ impl ComplexEnum<'_> { fn tagged_variant_tokens( tag: &str, variant_name: String, - variant_title: Option>, + variant_title: Option>, variant: &Variant, ) -> TokenStream { match &variant.fields { Fields::Named(named_fields) => { - let named_enum = NamedStructComponent { + let named_enum = NamedStructSchema { attributes: &variant.attrs, fields: &named_fields.named, generics: None, @@ -599,13 +598,13 @@ impl ToTokens for ComplexEnum<'_> { if self .attributes .iter() - .any(|attribute| attribute.path.get_ident().unwrap() == "component") + .any(|attribute| attribute.path.get_ident().unwrap() == "schema") { abort!( self.attributes.first().unwrap(), - "component macro attribute not expected on complex enum"; + "schema macro attribute not expected on complex enum"; - help = "Try adding the #[component(...)] on variant of the enum"; + help = "Try adding the #[schema(...)] on variant of the enum"; ); } @@ -635,8 +634,7 @@ impl ToTokens for ComplexEnum<'_> { let variant_name = rename_variant(&container_rules, &mut variant_serde_rules, variant_name) .unwrap_or_else(|| String::from(variant_name)); - let variant_title = - attr::parse_component_attr::>(&variant.attrs); + let variant_title = attr::parse_schema_attr::>(&variant.attrs); if let Some(tag) = &tag { Self::tagged_variant_tokens(tag, variant_name, variant_title, variant) @@ -671,24 +669,24 @@ impl ToTokens for ComplexEnum<'_> { struct TypeTuple<'a, T>(T, &'a Ident); #[cfg_attr(feature = "debug", derive(Debug))] -struct ComponentProperty<'a, T> { - component_part: &'a TypeTree<'a>, +struct SchemaProperty<'a, T> { + schema_part: &'a TypeTree<'a>, comments: Option<&'a CommentAttributes>, - attrs: Option<&'a ComponentAttr>, + attrs: Option<&'a SchemaAttr>, deprecated: Option<&'a Deprecated>, xml: Option<&'a Xml>, } -impl<'a, T: Sized + ToTokens> ComponentProperty<'a, T> { +impl<'a, T: Sized + ToTokens> SchemaProperty<'a, T> { fn new( - component_part: &'a TypeTree<'a>, + schema_part: &'a TypeTree<'a>, comments: Option<&'a CommentAttributes>, - attrs: Option<&'a ComponentAttr>, + attrs: Option<&'a SchemaAttr>, deprecated: Option<&'a Deprecated>, xml: Option<&'a Xml>, ) -> Self { Self { - component_part, + schema_part, comments, attrs, deprecated, @@ -698,29 +696,29 @@ impl<'a, T: Sized + ToTokens> ComponentProperty<'a, T> { /// Check wheter property is required or not fn is_option(&self) -> bool { - matches!(self.component_part.generic_type, Some(GenericType::Option)) + matches!(self.schema_part.generic_type, Some(GenericType::Option)) } } -impl ToTokens for ComponentProperty<'_, T> +impl ToTokens for SchemaProperty<'_, T> where T: Sized + quote::ToTokens + IsInline, { fn to_tokens(&self, tokens: &mut TokenStream) { - match self.component_part.generic_type { + match self.schema_part.generic_type { Some(GenericType::Map) => { // Maps are treated as generic objects with no named properties and // additionalProperties denoting the type - // maps have 2 child components and we are interested the second one of them + // maps have 2 child schemas and we are interested the second one of them // which is used to determine the additional properties - let component_property = ComponentProperty::new( - self.component_part + let schema_property = SchemaProperty::new( + self.schema_part .children .as_ref() - .expect("ComponentProperty Map type should have children") + .expect("SchemaProperty Map type should have children") .iter() .nth(1) - .expect("ComponentProperty Map type should have 2 child"), + .expect("SchemaProperty Map type should have 2 child"), self.comments, self.attrs, self.deprecated, @@ -728,7 +726,7 @@ where ); tokens.extend(quote! { - utoipa::openapi::ObjectBuilder::new().additional_properties(Some(#component_property)) + utoipa::openapi::ObjectBuilder::new().additional_properties(Some(#schema_property)) }); if let Some(description) = self.comments.and_then(|attributes| attributes.0.first()) @@ -739,14 +737,14 @@ where } } Some(GenericType::Vec) => { - let component_property = ComponentProperty::new( - self.component_part + let schema_property = SchemaProperty::new( + self.schema_part .children .as_ref() - .expect("ComponentProperty Vec should have children") + .expect("SchemaProperty Vec should have children") .iter() .next() - .expect("ComponentProperty Vec should have 1 child"), + .expect("SchemaProperty Vec should have 1 child"), self.comments, self.attrs, self.deprecated, @@ -755,7 +753,7 @@ where tokens.extend(quote! { utoipa::openapi::schema::ArrayBuilder::new() - .items(#component_property) + .items(#schema_property) }); if let Some(xml_value) = self.xml { @@ -771,35 +769,35 @@ where | Some(GenericType::Cow) | Some(GenericType::Box) | Some(GenericType::RefCell) => { - let component_property = ComponentProperty::new( - self.component_part + let schema_property = SchemaProperty::new( + self.schema_part .children .as_ref() - .expect("ComponentProperty generic container type should have children") + .expect("SchemaProperty generic container type should have children") .iter() .next() - .expect("ComponentProperty generic container type should have 1 child"), + .expect("SchemaProperty generic container type should have 1 child"), self.comments, self.attrs, self.deprecated, self.xml, ); - tokens.extend(component_property.into_token_stream()) + tokens.extend(schema_property.into_token_stream()) } None => { - let type_tree = self.component_part; + let type_tree = self.schema_part; match type_tree.value_type { ValueType::Primitive => { let type_path = &**type_tree.path.as_ref().unwrap(); - let component_type = ComponentType(type_path); + let schema_type = SchemaType(type_path); tokens.extend(quote! { - utoipa::openapi::PropertyBuilder::new().component_type(#component_type) + utoipa::openapi::PropertyBuilder::new().schema_type(#schema_type) }); - let format: ComponentFormat = (type_path).into(); + let format: SchemaFormat = (type_path).into(); if format.is_known_format() { tokens.extend(quote! { .format(Some(#format)) @@ -844,12 +842,12 @@ where let type_path = &**type_tree.path.as_ref().unwrap(); if is_inline { tokens.extend(quote_spanned! {type_path.span() => - <#type_path as utoipa::Component>::component() + <#type_path as utoipa::ToSchema>::schema() }); } else { let name = format_path_ref(type_path); tokens.extend(quote! { - utoipa::openapi::Ref::from_component_name(#name) + utoipa::openapi::Ref::from_schema_name(#name) }) } } @@ -862,7 +860,7 @@ where } } -/// Reformat a path reference string that was generated using [`quote`] to be used as a nice compact component reference, +/// Reformat a path reference string that was generated using [`quote`] to be used as a nice compact schema reference, /// by removing spaces between colon punctuation and `::` and the path segments. pub(crate) fn format_path_ref(path: &TypePath) -> String { let mut path: TypePath = path.clone(); @@ -932,13 +930,13 @@ fn rename<'a>( } #[cfg_attr(feature = "debug", derive(Debug))] -pub struct AliasComponent { +pub struct AliasSchema { pub name: String, pub ty: Ident, pub generics: Generics, } -impl Parse for AliasComponent { +impl Parse for AliasSchema { fn parse(input: syn::parse::ParseStream) -> syn::Result { let name = input.parse::()?; input.parse::()?; @@ -951,13 +949,13 @@ impl Parse for AliasComponent { } } -fn parse_aliases(attributes: &[Attribute]) -> Option> { +fn parse_aliases(attributes: &[Attribute]) -> Option> { attributes .iter() .find(|attribute| attribute.path.is_ident("aliases")) .map(|aliases| { aliases - .parse_args_with(Punctuated::::parse_terminated) + .parse_args_with(Punctuated::::parse_terminated) .unwrap_or_abort() }) } diff --git a/utoipa-gen/src/schema/component/attr.rs b/utoipa-gen/src/schema/schema/attr.rs similarity index 92% rename from utoipa-gen/src/schema/component/attr.rs rename to utoipa-gen/src/schema/schema/attr.rs index 153c8a10..4ac720d9 100644 --- a/utoipa-gen/src/schema/component/attr.rs +++ b/utoipa-gen/src/schema/schema/attr.rs @@ -6,9 +6,9 @@ use quote::{quote, ToTokens}; use syn::{parenthesized, parse::Parse, Attribute, Error, Token}; use crate::{ - component_type::ComponentFormat, parse_utils, schema::{GenericType, TypeTree}, + schema_type::SchemaFormat, AnyValue, }; @@ -21,14 +21,14 @@ pub(super) trait IsInline { } #[cfg_attr(feature = "debug", derive(Debug))] -pub struct ComponentAttr +pub struct SchemaAttr where T: Sized, { inner: T, } -impl AsRef for ComponentAttr +impl AsRef for SchemaAttr where T: Sized, { @@ -37,7 +37,7 @@ where } } -impl IsInline for ComponentAttr +impl IsInline for SchemaAttr where T: IsInline, { @@ -86,7 +86,7 @@ impl IsInline for Struct { #[cfg_attr(feature = "debug", derive(Debug))] pub struct UnnamedFieldStruct<'c> { pub(super) value_type: Option, - format: Option>, + format: Option>, default: Option, example: Option, } @@ -102,7 +102,7 @@ impl IsInline for UnnamedFieldStruct<'_> { pub struct NamedField<'c> { example: Option, pub(super) value_type: Option, - format: Option>, + format: Option>, default: Option, write_only: Option, read_only: Option, @@ -117,7 +117,7 @@ impl IsInline for NamedField<'_> { } } -impl Parse for ComponentAttr { +impl Parse for SchemaAttr<Title> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: title"; let mut title_attr = Title::default(); @@ -143,7 +143,7 @@ impl Parse for ComponentAttr<Title> { } } -impl Parse for ComponentAttr<Enum> { +impl Parse for SchemaAttr<Enum> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: default, example"; @@ -180,9 +180,9 @@ impl Parse for ComponentAttr<Enum> { } } -impl ComponentAttr<Struct> { +impl SchemaAttr<Struct> { pub(super) fn from_attributes_validated(attributes: &[Attribute]) -> Option<Self> { - parse_component_attr::<ComponentAttr<Struct>>(attributes).map(|attrs| { + parse_schema_attr::<SchemaAttr<Struct>>(attributes).map(|attrs| { if let Some(ref wrapped_ident) = attrs .as_ref() .xml_attr @@ -199,7 +199,7 @@ impl ComponentAttr<Struct> { } } -impl Parse for ComponentAttr<Struct> { +impl Parse for SchemaAttr<Struct> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: title, example, xml"; @@ -216,7 +216,7 @@ impl Parse for ComponentAttr<Struct> { match name { "title" => { - parse_utils::parse_next_literal_str(input)?; // Handled by ComponentAttr<Title> + parse_utils::parse_next_literal_str(input)?; // Handled by SchemaAttr<Title> } "example" => { struct_.example = Some(parse_utils::parse_next(input, || { @@ -240,7 +240,7 @@ impl Parse for ComponentAttr<Struct> { } } -impl<'c> Parse for ComponentAttr<UnnamedFieldStruct<'c>> { +impl<'c> Parse for SchemaAttr<UnnamedFieldStruct<'c>> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: title, default, example, format, value_type"; @@ -257,7 +257,7 @@ impl<'c> Parse for ComponentAttr<UnnamedFieldStruct<'c>> { match name { "title" => { - parse_utils::parse_next_literal_str(input)?; // Handled by ComponentAttr<Title> + parse_utils::parse_next_literal_str(input)?; // Handled by SchemaAttr<Title> } "default" => { unnamed_struct.default = Some(parse_utils::parse_next(input, || { @@ -271,7 +271,7 @@ impl<'c> Parse for ComponentAttr<UnnamedFieldStruct<'c>> { } "format" => { unnamed_struct.format = Some(parse_utils::parse_next(input, || { - input.parse::<ComponentFormat<'c>>() + input.parse::<SchemaFormat<'c>>() })?) } "value_type" => { @@ -293,12 +293,12 @@ impl<'c> Parse for ComponentAttr<UnnamedFieldStruct<'c>> { } } -impl<'c> ComponentAttr<NamedField<'c>> { +impl<'c> SchemaAttr<NamedField<'c>> { pub(super) fn from_attributes_validated( attributes: &[Attribute], component_part: &TypeTree, ) -> Option<Self> { - parse_component_attr::<ComponentAttr<NamedField>>(attributes) + parse_schema_attr::<SchemaAttr<NamedField>>(attributes) .map(|attrs| { is_valid_xml_attr(&attrs, component_part); @@ -328,7 +328,7 @@ impl<'c> ComponentAttr<NamedField<'c>> { } #[inline] -fn is_valid_xml_attr(attrs: &ComponentAttr<NamedField>, component_part: &TypeTree) { +fn is_valid_xml_attr(attrs: &SchemaAttr<NamedField>, component_part: &TypeTree) { if !matches!( component_part.generic_type, Some(crate::schema::GenericType::Vec) @@ -346,7 +346,7 @@ fn is_valid_xml_attr(attrs: &ComponentAttr<NamedField>, component_part: &TypeTre } } -impl<'c> Parse for ComponentAttr<NamedField<'c>> { +impl<'c> Parse for SchemaAttr<NamedField<'c>> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: example, format, default, write_only, read_only, xml, value_type, inline"; let mut field = NamedField::default(); @@ -368,7 +368,7 @@ impl<'c> Parse for ComponentAttr<NamedField<'c>> { } "format" => { field.format = Some(parse_utils::parse_next(input, || { - input.parse::<ComponentFormat>() + input.parse::<SchemaFormat>() })?) } "default" => { @@ -401,14 +401,14 @@ impl<'c> Parse for ComponentAttr<NamedField<'c>> { } } -pub fn parse_component_attr<T: Sized + Parse>(attributes: &[Attribute]) -> Option<T> { +pub fn parse_schema_attr<T: Sized + Parse>(attributes: &[Attribute]) -> Option<T> { attributes .iter() - .find(|attribute| attribute.path.get_ident().unwrap() == "component") + .find(|attribute| attribute.path.get_ident().unwrap() == "schema") .map(|attribute| attribute.parse_args::<T>().unwrap_or_abort()) } -impl<T> ToTokens for ComponentAttr<T> +impl<T> ToTokens for SchemaAttr<T> where T: quote::ToTokens, { diff --git a/utoipa-gen/src/schema/component/xml.rs b/utoipa-gen/src/schema/schema/xml.rs similarity index 100% rename from utoipa-gen/src/schema/component/xml.rs rename to utoipa-gen/src/schema/schema/xml.rs diff --git a/utoipa-gen/src/component_type.rs b/utoipa-gen/src/schema_type.rs similarity index 81% rename from utoipa-gen/src/component_type.rs rename to utoipa-gen/src/schema_type.rs index 891d0a64..4491a392 100644 --- a/utoipa-gen/src/component_type.rs +++ b/utoipa-gen/src/schema_type.rs @@ -4,9 +4,9 @@ use quote::{quote, ToTokens}; use syn::{parse::Parse, Error, Ident, TypePath}; /// Tokenizes OpenAPI data type correctly according to the Rust type -pub struct ComponentType<'a>(pub &'a syn::TypePath); +pub struct SchemaType<'a>(pub &'a syn::TypePath); -impl ComponentType<'_> { +impl SchemaType<'_> { /// Check whether type is known to be primitive in wich case returns true. pub fn is_primitive(&self) -> bool { let last_segment = match self.0.path.segments.last() { @@ -108,7 +108,7 @@ fn is_primitive_rust_decimal(name: &str) -> bool { matches!(name, "Decimal") } -impl ToTokens for ComponentType<'_> { +impl ToTokens for SchemaType<'_> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { let last_segment = self.0.path.segments.last().unwrap_or_else(|| { abort_call_site!("expected there to be at least one segment in the path") @@ -117,43 +117,43 @@ impl ToTokens for ComponentType<'_> { match name { "String" | "str" | "char" => { - tokens.extend(quote! {utoipa::openapi::ComponentType::String}) + tokens.extend(quote! {utoipa::openapi::SchemaType::String}) } - "bool" => tokens.extend(quote! { utoipa::openapi::ComponentType::Boolean }), + "bool" => tokens.extend(quote! { utoipa::openapi::SchemaType::Boolean }), "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64" - | "u128" | "usize" => tokens.extend(quote! { utoipa::openapi::ComponentType::Integer }), - "f32" | "f64" => tokens.extend(quote! { utoipa::openapi::ComponentType::Number }), + | "u128" | "usize" => tokens.extend(quote! { utoipa::openapi::SchemaType::Integer }), + "f32" | "f64" => tokens.extend(quote! { utoipa::openapi::SchemaType::Number }), #[cfg(any(feature = "chrono", feature = "chrono_with_format"))] "DateTime" | "Date" | "Duration" => { - tokens.extend(quote! { utoipa::openapi::ComponentType::String }) + tokens.extend(quote! { utoipa::openapi::SchemaType::String }) } #[cfg(feature = "decimal")] - "Decimal" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }), + "Decimal" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), #[cfg(feature = "rocket_extras")] - "PathBuf" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }), + "PathBuf" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), #[cfg(feature = "uuid")] - "Uuid" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }), + "Uuid" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), #[cfg(feature = "time")] "Date" | "PrimitiveDateTime" | "OffsetDateTime" => { - tokens.extend(quote! { utoipa::openapi::ComponentType::String }) + tokens.extend(quote! { utoipa::openapi::SchemaType::String }) } #[cfg(feature = "time")] - "Duration" => tokens.extend(quote! { utoipa::openapi::ComponentType::String }), - _ => tokens.extend(quote! { utoipa::openapi::ComponentType::Object }), + "Duration" => tokens.extend(quote! { utoipa::openapi::SchemaType::String }), + _ => tokens.extend(quote! { utoipa::openapi::SchemaType::Object }), } } } -/// Either Rust type component variant or enum variant component variant. +/// Either Rust type component variant or enum variant schema variant. #[cfg_attr(feature = "debug", derive(Debug))] -pub enum ComponentFormat<'c> { - /// [`utoipa::openapi::shcema::ComponentFormat`] enum variant component format. +pub enum SchemaFormat<'c> { + /// [`utoipa::openapi::shcema::SchemaFormat`] enum variant schema format. Variant(Variant), - /// Rust type component format. + /// Rust type schema format. Type(Type<'c>), } -impl ComponentFormat<'_> { +impl SchemaFormat<'_> { pub fn is_known_format(&self) -> bool { match self { Self::Type(ty) => ty.is_known_format(), @@ -162,19 +162,19 @@ impl ComponentFormat<'_> { } } -impl<'a> From<&'a TypePath> for ComponentFormat<'a> { +impl<'a> From<&'a TypePath> for SchemaFormat<'a> { fn from(type_path: &'a TypePath) -> Self { Self::Type(Type(type_path)) } } -impl Parse for ComponentFormat<'_> { +impl Parse for SchemaFormat<'_> { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { Ok(Self::Variant(input.parse()?)) } } -impl ToTokens for ComponentFormat<'_> { +impl ToTokens for SchemaFormat<'_> { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Type(ty) => ty.to_tokens(tokens), @@ -242,28 +242,28 @@ impl ToTokens for Type<'_> { match name { "i8" | "i16" | "i32" | "u8" | "u16" | "u32" => { - tokens.extend(quote! { utoipa::openapi::ComponentFormat::Int32 }) + tokens.extend(quote! { utoipa::openapi::SchemaFormat::Int32 }) } - "i64" | "u64" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Int64 }), - "f32" | "f64" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Float }), + "i64" | "u64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::Int64 }), + "f32" | "f64" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::Float }), #[cfg(feature = "chrono_with_format")] - "DateTime" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::DateTime }), + "DateTime" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::DateTime }), #[cfg(feature = "chrono_with_format")] - "Date" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Date }), + "Date" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::Date }), #[cfg(feature = "uuid")] - "Uuid" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Uuid }), + "Uuid" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::Uuid }), #[cfg(feature = "time")] - "Date" => tokens.extend(quote! { utoipa::openapi::ComponentFormat::Date }), + "Date" => tokens.extend(quote! { utoipa::openapi::SchemaFormat::Date }), #[cfg(feature = "time")] "PrimitiveDateTime" | "OffsetDateTime" => { - tokens.extend(quote! { utoipa::openapi::ComponentFormat::DateTime }) + tokens.extend(quote! { utoipa::openapi::SchemaFormat::DateTime }) } _ => (), } } } -/// [`Parse`] and [`ToTokens`] implementation for [`utoipa::openapi::schema::ComponentFormat`]. +/// [`Parse`] and [`ToTokens`] implementation for [`utoipa::openapi::schema::SchemaFormat`]. #[cfg_attr(feature = "debug", derive(Debug))] pub enum Variant { Int32, @@ -328,21 +328,21 @@ impl Parse for Variant { impl ToTokens for Variant { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - Self::Int32 => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Int32)), - Self::Int64 => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Int64)), - Self::Float => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Float)), - Self::Double => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Double)), - Self::Byte => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Byte)), - Self::Binary => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Binary)), - Self::Date => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Date)), + Self::Int32 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Int32)), + Self::Int64 => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Int64)), + Self::Float => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Float)), + Self::Double => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Double)), + Self::Byte => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Byte)), + Self::Binary => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Binary)), + Self::Date => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Date)), Self::DateTime => { - tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::DateTime)) + tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::DateTime)) } Self::Password => { - tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Password)) + tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Password)) } #[cfg(feature = "uuid")] - Self::Uuid => tokens.extend(quote!(utoipa::openapi::schema::ComponentFormat::Uuid)), + Self::Uuid => tokens.extend(quote!(utoipa::openapi::schema::SchemaFormat::Uuid)), }; } }