diff --git a/src/lib.rs b/src/lib.rs index 950359dd..9edaf1f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -322,17 +322,18 @@ pub trait Component { /// utoipa::openapi::PathItemType::Get, /// utoipa::openapi::path::Operation::new() /// .with_responses( -/// utoipa::openapi::Responses::new() -/// .with_response( +/// utoipa::openapi::ResponsesBuilder::new() +/// .response( /// "200", -/// utoipa::openapi::Response::new("Pet found succesfully").with_content( -/// "application/json", -/// utoipa::openapi::Content::new( -/// utoipa::openapi::Ref::from_component_name("Pet"), -/// ), -/// ), +/// utoipa::openapi::ResponseBuilder::new() +/// .description("Pet found succesfully") +/// .content("application/json", +/// utoipa::openapi::Content::new( +/// utoipa::openapi::Ref::from_component_name("Pet"), +/// ), +/// ).build(), /// ) -/// .with_response("404", utoipa::openapi::Response::new("Pet was not found")), +/// .response("404", utoipa::openapi::Response::new("Pet was not found")).build(), /// ) /// .with_operation_id("get_pet_by_id") /// .with_deprecated(utoipa::openapi::Deprecated::False) diff --git a/src/openapi.rs b/src/openapi.rs index bb157c87..d8b17cce 100644 --- a/src/openapi.rs +++ b/src/openapi.rs @@ -3,12 +3,12 @@ use std::collections::BTreeMap; use serde::{de::Visitor, Deserialize, Serialize, Serializer}; pub use self::{ - content::Content, + content::{Content, ContentBuilder}, external_docs::ExternalDocs, - header::Header, + header::{Header, HeaderBuilder}, info::{Contact, ContactBuilder, Info, InfoBuilder, License, LicenseBuilder}, path::{PathItem, PathItemType, Paths}, - response::{Response, Responses}, + response::{Response, ResponseBuilder, Responses, ResponsesBuilder}, schema::{ Array, Component, ComponentFormat, ComponentType, Components, Object, OneOf, Property, Ref, ToArray, diff --git a/src/openapi/content.rs b/src/openapi/content.rs index c8baee9d..153d92fd 100644 --- a/src/openapi/content.rs +++ b/src/openapi/content.rs @@ -1,20 +1,25 @@ +//! Implements content object for request body and response. use serde::{Deserialize, Serialize}; #[cfg(feature = "serde_json")] use serde_json::Value; -use super::Component; +use super::{add_value, build_fn, from, new, Component}; +/// Content holds request body content or response content. #[derive(Serialize, Deserialize, Default, Clone)] #[cfg_attr(feature = "debug", derive(Debug))] #[non_exhaustive] pub struct Content { + /// Schema used in response body or request body. pub schema: Component, + /// Example for request body or response body. #[serde(skip_serializing_if = "Option::is_none")] #[cfg(feature = "serde_json")] pub example: Option, + /// Example for request body or response body. #[serde(skip_serializing_if = "Option::is_none")] #[cfg(not(feature = "serde_json"))] pub example: Option, @@ -27,18 +32,41 @@ impl Content { example: None, } } +} + +/// Builder for [`Content`] with chainable configuration methods to create a new [`Content`]. +#[derive(Default)] +pub struct ContentBuilder { + schema: Component, #[cfg(feature = "serde_json")] - pub fn with_example(mut self, example: Value) -> Self { - self.example = Some(example); + example: Option, + + #[cfg(not(feature = "serde_json"))] + example: Option, +} + +from!(Content ContentBuilder schema, example); - self +impl ContentBuilder { + new!(pub ContentBuilder); + + /// Add schema. + pub fn schema>(mut self, component: I) -> Self { + add_value!(self schema component.into()) } - #[cfg(not(feature = "serde_json"))] - pub fn with_example>(mut self, example: S) -> Self { - self.example = Some(example.into()); + /// Add example of schema. + #[cfg(feature = "serde_json")] + pub fn example(mut self, example: Option) -> Self { + add_value!(self example example) + } - self + /// Add example of schema. + #[cfg(not(feature = "serde_json"))] + pub fn example>(mut self, example: Option) -> Self { + add_value!(self example example.map(|example| example.into())) } + + build_fn!(pub Content schema, example); } diff --git a/src/openapi/external_docs.rs b/src/openapi/external_docs.rs index cf3df70f..d10a18c1 100644 --- a/src/openapi/external_docs.rs +++ b/src/openapi/external_docs.rs @@ -1,25 +1,53 @@ +//! Implements [OpenAPI External Docs Object][external_docs] types. +//! +//! [external_docs]: https://spec.openapis.org/oas/latest.html#xml-object use serde::{Deserialize, Serialize}; -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -#[serde(rename_all = "camelCase")] -pub struct ExternalDocs { - pub url: String, - pub description: Option, +use super::{add_value, build_fn, builder, from, new}; + +builder! { + ExternalDocsBuilder; + + /// Reference of external resource allowing extended documentation. + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + #[serde(rename_all = "camelCase")] + pub struct ExternalDocs { + /// Target url for external documentation location. + pub url: String, + /// Additional description supporting markdown syntax of the external documentation. + pub description: Option, + } } impl ExternalDocs { + /// Construct a new [`ExternalDocs`]. + /// + /// Function takes target url argument for the external documentation location. + /// + /// # Examples + /// + /// ```rust + /// # use utoipa::openapi::external_docs::ExternalDocs; + /// let external_docs = ExternalDocs::new("https://pet-api.external.docs"); + /// ``` pub fn new>(url: S) -> Self { Self { url: url.as_ref().to_string(), ..Default::default() } } +} - pub fn with_description>(mut self, description: S) -> Self { - self.description = Some(description.as_ref().to_string()); +impl ExternalDocsBuilder { + /// Add target url for external documentation location. + pub fn url>(mut self, url: I) -> Self { + add_value!(self url url.into()) + } - self + /// Add additional description of external documentation. + pub fn description>(mut self, description: Option) -> Self { + add_value!(self description description.map(|description| description.into())) } } diff --git a/src/openapi/header.rs b/src/openapi/header.rs index 687cea88..5c101e0a 100644 --- a/src/openapi/header.rs +++ b/src/openapi/header.rs @@ -1,30 +1,55 @@ +//! Implements [OpenAPI Header Object][header] types. +//! +//! [header]: https://spec.openapis.org/oas/latest.html#header-object + use serde::{Deserialize, Serialize}; -use super::{Component, ComponentType, Property}; +use super::{add_value, build_fn, builder, from, new, Component, ComponentType, Property}; + +builder! { + HeaderBuilder; -#[non_exhaustive] -#[derive(Serialize, Deserialize, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Header { - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, + /// Implements [OpenAPI Header Object][header] for response headers. + /// + /// [header]: https://spec.openapis.org/oas/latest.html#header-object + #[non_exhaustive] + #[derive(Serialize, Deserialize, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Header { + /// Schema of header type. + pub schema: Component, - pub schema: Component, + /// Additional descripiton of the header value. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + } } impl Header { + /// Construct a new [`Header`] with custom schema. If you wish to construct a default + /// header with `String` type you can use [`Header::default`] function. + /// + /// # Examples + /// + /// 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)); + /// ``` + /// + /// 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 { Self { schema: component.into(), ..Default::default() } } - - pub fn with_description>(mut self, description: S) -> Self { - self.description = Some(description.as_ref().to_string()); - - self - } } impl Default for Header { @@ -35,3 +60,15 @@ impl Default for Header { } } } + +impl HeaderBuilder { + /// Add schema of header. + pub fn schema>(mut self, component: I) -> Self { + add_value!(self schema component.into()) + } + + /// Add additional description for header. + pub fn description>(mut self, description: Option) -> Self { + add_value!(self description description.map(|description| description.into())) + } +} diff --git a/src/openapi/path.rs b/src/openapi/path.rs index 17777897..b18774c1 100644 --- a/src/openapi/path.rs +++ b/src/openapi/path.rs @@ -239,8 +239,8 @@ impl Operation { self } - pub fn with_response>(mut self, code: S, response: Response) -> Self { - self.responses = self.responses.with_response(code, response); + pub fn with_response>(mut self, code: S, response: Response) -> Self { + self.responses.responses.insert(code.into(), response); self } diff --git a/src/openapi/request_body.rs b/src/openapi/request_body.rs index 5fe6ef06..0bb4896f 100644 --- a/src/openapi/request_body.rs +++ b/src/openapi/request_body.rs @@ -1,43 +1,57 @@ +//! Implements [OpenAPI Request Body][request_body] types. +//! +//! [request_body]: https://spec.openapis.org/oas/latest.html#request-body-object use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use super::{Content, Required}; - -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -#[serde(rename_all = "camelCase")] -pub struct RequestBody { - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - - pub content: HashMap, - - #[serde(skip_serializing_if = "Option::is_none")] - pub required: Option, +use super::{add_value, build_fn, builder, from, new, Content, Required}; + +builder! { + RequestBodyBuilder; + + /// Implements [OpenAPI Request Body][request_body]. + /// + /// [request_body]: https://spec.openapis.org/oas/latest.html#request-body-object + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + #[serde(rename_all = "camelCase")] + pub struct RequestBody { + /// Additional description of [`RequestBody`] supporting markdown syntax. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + + /// Map of request body contents mapped by content type e.g. `application/json`. + pub content: HashMap, + + /// Determines whether request body is reuqired in the request or not. + #[serde(skip_serializing_if = "Option::is_none")] + pub required: Option, + } } impl RequestBody { + /// Constrcut a new [`RequestBody`]. pub fn new() -> Self { Default::default() } +} - pub fn with_description>(mut self, description: S) -> Self { - self.description = Some(description.as_ref().to_string()); - - self +impl RequestBodyBuilder { + /// Add description for [`RequestBody`]. + pub fn description>(mut self, description: Option) -> Self { + add_value!(self description description.map(|description| description.into())) } - pub fn with_required(mut self, required: Required) -> Self { - self.required = Some(required); - - self + /// Define [`RequestBody`] required. + pub fn required(mut self, required: Option) -> Self { + add_value!(self required required) } - pub fn with_content>(mut self, content_type: S, content: Content) -> Self { - self.content - .insert(content_type.as_ref().to_string(), content); + /// Add [`Content`] by content type e.g `application/json` to [`RequestBody`]. + pub fn content>(mut self, content_type: S, content: Content) -> Self { + self.content.insert(content_type.into(), content); self } diff --git a/src/openapi/response.rs b/src/openapi/response.rs index 4c006ff4..08ca5178 100644 --- a/src/openapi/response.rs +++ b/src/openapi/response.rs @@ -1,61 +1,114 @@ +//! Implements [OpenApi Responses][responses]. +//! +//! [responses]: https://spec.openapis.org/oas/latest.html#responses-object use std::collections::{BTreeMap, HashMap}; use serde::{Deserialize, Serialize}; -use super::{header::Header, Content}; +use super::{add_value, build_fn, builder, from, header::Header, new, Content}; -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -#[serde(rename_all = "camelCase")] -pub struct Responses { - #[serde(flatten)] - pub inner: BTreeMap, +builder! { + ResponsesBuilder; + + /// Implements [OpenAPI Responses Object][responses]. + /// + /// Responses is a map holding api operation responses identified by their status code. + /// + /// [responses]: https://spec.openapis.org/oas/latest.html#responses-object + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + #[serde(rename_all = "camelCase")] + pub struct Responses { + /// Map containing status code as a key with represented response as a value. + #[serde(flatten)] + pub responses: BTreeMap, + } } impl Responses { pub fn new() -> Self { Default::default() } +} - pub fn with_response>(mut self, code: S, response: Response) -> Self { - self.inner.insert(code.as_ref().to_string(), response); +impl ResponsesBuilder { + /// Add response to responses. + pub fn response>(mut self, code: S, response: Response) -> Self { + self.responses.insert(code.into(), response); self } } -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -#[serde(rename_all = "camelCase")] -pub struct Response { - pub description: String, +impl FromIterator<(C, Response)> for Responses +where + C: Into, +{ + fn from_iter>(iter: T) -> Self { + Self { + responses: BTreeMap::from_iter( + iter.into_iter() + .map(|(code, response)| (code.into(), response)), + ), + } + } +} - #[serde(skip_serializing_if = "HashMap::is_empty")] - pub headers: HashMap, +builder! { + ResponseBuilder; - #[serde(skip_serializing_if = "HashMap::is_empty")] - pub content: HashMap, + /// Implements [OpenAPI Response Object][response]. + /// + /// Response is api operation response. + /// + /// [response]: https://spec.openapis.org/oas/latest.html#response-object + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + #[serde(rename_all = "camelCase")] + pub struct Response { + /// Description of the response. Response support markdown syntax. + pub description: String, + + /// Map of headers identified by their name. `Content-Type` header will be ignored. + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub headers: HashMap, + + /// Map of response [`Content`] objects identified by response body content type e.g `application/json`. + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub content: HashMap, + } } impl Response { - pub fn new>(description: S) -> Self { + /// Construct a new [`Response`]. + /// + /// Function takes description as argument. + pub fn new>(description: S) -> Self { Self { - description: description.as_ref().to_string(), + description: description.into(), ..Default::default() } } +} + +impl ResponseBuilder { + /// Add description. Description supports markdown syntax. + pub fn description>(mut self, description: I) -> Self { + add_value!(self description description.into()) + } - pub fn with_content>(mut self, content_type: S, content: Content) -> Self { - self.content - .insert(content_type.as_ref().to_string(), content); + /// Add [`Content`] of the [`Response`] with content type e.g `application/json`. + pub fn content>(mut self, content_type: S, content: Content) -> Self { + self.content.insert(content_type.into(), content); self } - pub fn with_header>(mut self, name: S, header: Header) -> Self { - self.headers.insert(name.as_ref().to_string(), header); + /// Add response [`Header`]. + pub fn header>(mut self, name: S, header: Header) -> Self { + self.headers.insert(name.into(), header); self } diff --git a/src/openapi/tag.rs b/src/openapi/tag.rs index ea0ec980..9cbed0a2 100644 --- a/src/openapi/tag.rs +++ b/src/openapi/tag.rs @@ -1,43 +1,59 @@ +//! Implements [OpenAPI Tag Object][tag] types. +//! +//! [tag]: https://spec.openapis.org/oas/latest.html#tag-object use serde::{Deserialize, Serialize}; -use super::external_docs::ExternalDocs; - -/// Implements [OpenAPI Tag Object][tag]. -/// -/// Tag can be used to provide additional metadata for tags used by path operations. -/// -/// [tag]: https://spec.openapis.org/oas/latest.html#tag-object -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -#[serde(rename_all = "camelCase")] -pub struct Tag { - pub name: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub external_docs: Option, +use super::{add_value, build_fn, builder, external_docs::ExternalDocs, from, new}; + +builder! { + TagBuilder; + + /// Implements [OpenAPI Tag Object][tag]. + /// + /// Tag can be used to provide additional metadata for tags used by path operations. + /// + /// [tag]: https://spec.openapis.org/oas/latest.html#tag-object + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + #[serde(rename_all = "camelCase")] + pub struct Tag { + /// Name of the tag. Should match to tag of **operation**. + pub name: String, + + /// Additional description for the tag shown in the document. + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + + /// Additional external documentation for the tag. + #[serde(skip_serializing_if = "Option::is_none")] + pub external_docs: Option, + } } impl Tag { + /// Construct a new [`Tag`] with given name. pub fn new>(name: S) -> Self { Self { name: name.as_ref().to_string(), ..Default::default() } } +} - pub fn with_description>(mut self, description: S) -> Self { - self.description = Some(description.as_ref().to_string()); - - self +impl TagBuilder { + /// Add name fo the tag. + pub fn name>(mut self, name: I) -> Self { + add_value!(self name name.into()) } - pub fn with_external_docs(mut self, external_docs: ExternalDocs) -> Self { - self.external_docs = Some(external_docs); + /// Add additional description for the tag. + pub fn description>(mut self, description: Option) -> Self { + add_value!(self description description.map(|description| description.into())) + } - self + /// Add additional external documentation for the tag. + pub fn external_docs(mut self, external_docs: Option) -> Self { + add_value!(self external_docs external_docs) } } diff --git a/src/openapi/xml.rs b/src/openapi/xml.rs index d18d6be8..245bc28f 100644 --- a/src/openapi/xml.rs +++ b/src/openapi/xml.rs @@ -5,40 +5,55 @@ use std::borrow::Cow; use serde::{Deserialize, Serialize}; -/// Implements [OpenAPI Xml Object][xml_object]. -/// -/// Can be used to modify xml output format of specific [OpenAPI Schema Object][schema_object] which are -/// implemented in [`schema`][schema] module. -/// -/// [xml_object]: https://spec.openapis.org/oas/latest.html#xml-object -/// [schema_object]: https://spec.openapis.org/oas/latest.html#schema-object -/// [schema]: ../schema/index.html -#[non_exhaustive] -#[derive(Serialize, Deserialize, Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Xml { - /// Used to replace the name of attribute or type used in schema property. - /// When used with [`Xml::wrapped`] attribute the name will be used as a wrapper name - /// for wrapped array instead of the item or type name. - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option>, +use super::{add_value, build_fn, builder, from, new}; - /// Valid uri definition of namespace used in xml. - #[serde(skip_serializing_if = "Option::is_none")] - pub namespace: Option>, - - /// Prefix for xml element [`Xml::name`]. - #[serde(skip_serializing_if = "Option::is_none")] - pub prefix: Option>, - - /// Flag deciding will this attribute translate to element attribute instead of xml element. - #[serde(skip_serializing_if = "Option::is_none")] - pub attribute: Option, - - /// Flag only usable with array definition. If set to true the ouput xml will wrap the array of items - /// `` instead of unwrapped ``. - #[serde(skip_serializing_if = "Option::is_none")] - pub wrapped: Option, +builder! { + /// # Examples + /// + /// Create [`Xml`] with [`XmlBuilder`]. + /// ```rust + /// # use utoipa::openapi::xml::XmlBuilder; + /// let xml = XmlBuilder::new() + /// .name(Some("some_name")) + /// .prefix(Some("prefix")) + /// .build(); + /// ``` + XmlBuilder; + /// Implements [OpenAPI Xml Object][xml_object]. + /// + /// Can be used to modify xml output format of specific [OpenAPI Schema Object][schema_object] which are + /// implemented in [`schema`][schema] module. + /// + /// [xml_object]: https://spec.openapis.org/oas/latest.html#xml-object + /// [schema_object]: https://spec.openapis.org/oas/latest.html#schema-object + /// [schema]: ../schema/index.html + #[non_exhaustive] + #[derive(Serialize, Deserialize, Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Xml { + /// Used to replace the name of attribute or type used in schema property. + /// When used with [`Xml::wrapped`] attribute the name will be used as a wrapper name + /// for wrapped array instead of the item or type name. + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option>, + + /// Valid uri definition of namespace used in xml. + #[serde(skip_serializing_if = "Option::is_none")] + pub namespace: Option>, + + /// Prefix for xml element [`Xml::name`]. + #[serde(skip_serializing_if = "Option::is_none")] + pub prefix: Option>, + + /// Flag deciding will this attribute translate to element attribute instead of xml element. + #[serde(skip_serializing_if = "Option::is_none")] + pub attribute: Option, + + /// Flag only usable with array definition. If set to true the ouput xml will wrap the array of items + /// `` instead of unwrapped ``. + #[serde(skip_serializing_if = "Option::is_none")] + pub wrapped: Option, + } } impl Xml { @@ -48,49 +63,41 @@ impl Xml { ..Default::default() } } +} +impl XmlBuilder { /// Add [`Xml::name`] to xml object. /// /// Builder style chainable consuming add name method. pub fn name>>(mut self, name: Option) -> Self { - self.name = name.map(|name| name.into()); - - self + add_value!(self name name.map(|name| name.into())) } /// Add [`Xml::namespace`] to xml object. /// /// Builder style chainable consuming add namespace method. pub fn namespace>>(mut self, namespace: Option) -> Self { - self.namespace = namespace.map(|namespace| namespace.into()); - - self + add_value!(self namespace namespace.map(|namespace| namespace.into())) } /// Add [`Xml::prefix`] to xml object. /// /// Builder style chainable consuming add prefix method. pub fn prefix>>(mut self, prefix: Option) -> Self { - self.prefix = prefix.map(|prefix| prefix.into()); - - self + add_value!(self prefix prefix.map(|prefix| prefix.into())) } /// Mark [`Xml`] object as attribute. See [`Xml::attribute`] /// /// Builder style chainable consuming add attribute method. pub fn attribute(mut self, attribute: Option) -> Self { - self.attribute = attribute; - - self + add_value!(self attribute attribute) } /// Mark [`Xml`] object wrapped. See [`Xml::wrapped`] /// /// Builder style chainable consuming add wrapped method. pub fn wrapped(mut self, wrapped: Option) -> Self { - self.wrapped = wrapped; - - self + add_value!(self wrapped wrapped) } } diff --git a/utoipa-gen/src/component/xml.rs b/utoipa-gen/src/component/xml.rs index f89e4001..ac810ff3 100644 --- a/utoipa-gen/src/component/xml.rs +++ b/utoipa-gen/src/component/xml.rs @@ -89,7 +89,7 @@ impl Parse for XmlAttr { impl ToTokens for XmlAttr { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { tokens.extend(quote! { - utoipa::openapi::xml::Xml::new() + utoipa::openapi::xml::XmlBuilder::new() }); if let Some(ref name) = self.name { @@ -128,6 +128,8 @@ impl ToTokens for XmlAttr { }) } } + + tokens.extend(quote! { .build() }) } } diff --git a/utoipa-gen/src/lib.rs b/utoipa-gen/src/lib.rs index 462d92f9..6558ac86 100644 --- a/utoipa-gen/src/lib.rs +++ b/utoipa-gen/src/lib.rs @@ -814,14 +814,17 @@ impl ToTokens for ExternalDocs { fn to_tokens(&self, tokens: &mut TokenStream2) { let url = &self.url; tokens.extend(quote! { - utoipa::openapi::external_docs::ExternalDocs::new(#url) + utoipa::openapi::external_docs::ExternalDocsBuilder::new() + .url(#url) }); if let Some(ref description) = self.description { tokens.extend(quote! { - .with_description(#description) + .description(Some(#description)) }); } + + tokens.extend(quote! { .build() }) } } diff --git a/utoipa-gen/src/openapi.rs b/utoipa-gen/src/openapi.rs index 65c3aa95..8ac26953 100644 --- a/utoipa-gen/src/openapi.rs +++ b/utoipa-gen/src/openapi.rs @@ -183,20 +183,22 @@ impl ToTokens for Tag { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.name; tokens.extend(quote! { - utoipa::openapi::tag::Tag::new(#name) + utoipa::openapi::tag::TagBuilder::new().name(#name) }); if let Some(ref description) = self.description { tokens.extend(quote! { - .with_description(#description) + .description(Some(#description)) }); } if let Some(ref external_docs) = self.external_docs { tokens.extend(quote! { - .with_external_docs(#external_docs) + .external_docs(Some(#external_docs)) }); } + + tokens.extend(quote! { .build() }) } } diff --git a/utoipa-gen/src/path/request_body.rs b/utoipa-gen/src/path/request_body.rs index c6e9ce03..cfe64c5f 100644 --- a/utoipa-gen/src/path/request_body.rs +++ b/utoipa-gen/src/path/request_body.rs @@ -130,16 +130,18 @@ impl ToTokens for RequestBodyAttr { let required: Required = (!body_type.is_option).into(); tokens.extend(quote! { - utoipa::openapi::request_body::RequestBody::new() - .with_content(#content_type, utoipa::openapi::Content::new(#property)) - .with_required(#required) + utoipa::openapi::request_body::RequestBodyBuilder::new() + .content(#content_type, utoipa::openapi::Content::new(#property)) + .required(Some(#required)) }); } if let Some(ref description) = self.description { tokens.extend(quote! { - .with_description(#description) + .description(Some(#description)) }) } + + tokens.extend(quote! { .build() }) } } diff --git a/utoipa-gen/src/path/response.rs b/utoipa-gen/src/path/response.rs index 3482ba1a..140c9616 100644 --- a/utoipa-gen/src/path/response.rs +++ b/utoipa-gen/src/path/response.rs @@ -108,7 +108,7 @@ impl ToTokens for Response { fn to_tokens(&self, tokens: &mut TokenStream2) { let description = &self.description; tokens.extend(quote! { - utoipa::openapi::Response::new(#description) + utoipa::openapi::ResponseBuilder::new().description(#description) }); if let Some(ref body_type) = self.response_type { @@ -116,25 +116,25 @@ impl ToTokens for Response { let component = Property::new(body_type.is_array, body_ty); let mut content = quote! { - utoipa::openapi::Content::new(#component) + utoipa::openapi::ContentBuilder::new().schema(#component) }; if let Some(ref example) = self.example { content.extend(quote! { - .with_example(#example) + .example(Some(#example)) }) } if let Some(content_types) = self.content_type.as_ref() { content_types.iter().for_each(|content_type| { tokens.extend(quote! { - .with_content(#content_type, #content) + .content(#content_type, #content.build()) }) }) } else { let default_type = self.resolve_content_type(None, &component.component_type); tokens.extend(quote! { - .with_content(#default_type, #content) + .content(#default_type, #content.build()) }); } } @@ -142,9 +142,11 @@ impl ToTokens for Response { self.headers.iter().for_each(|header| { let name = &header.name; tokens.extend(quote! { - .with_header(#name, #header) + .header(#name, #header) }) }); + + tokens.extend(quote! { .build() }) } } @@ -154,14 +156,20 @@ pub struct Responses<'a>(pub &'a [Response]); impl ToTokens for Responses<'_> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - tokens.extend(quote! { utoipa::openapi::Responses::new() }); + if self.0.is_empty() { + tokens.extend(quote! { utoipa::openapi::Responses::new() }) + } else { + let responses = self.0.iter().fold(quote! {}, |mut acc, response| { + let code = &response.status_code.to_string(); + acc.extend(quote! { (#code, #response), }); + + acc + }); - self.0.iter().for_each(|response| { - let status = response.status_code.to_string(); tokens.extend(quote! { - .with_response(#status, #response) + utoipa::openapi::Responses::from_iter([#responses]) }); - }) + } } } @@ -282,19 +290,21 @@ impl ToTokens for Header { let header_type = Property::new(header_type.is_array, &header_type.ty); tokens.extend(quote! { - utoipa::openapi::Header::new(#header_type) + utoipa::openapi::HeaderBuilder::new().schema(#header_type) }) } else { // default header (string type) tokens.extend(quote! { - utoipa::openapi::Header::default() + Into::::into(utoipa::openapi::Header::default()) }) }; if let Some(ref description) = self.description { tokens.extend(quote! { - .with_description(#description) + .description(Some(#description)) }) } + + tokens.extend(quote! { .build() }) } }