Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore more builders #47

Merged
merged 2 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
44 changes: 36 additions & 8 deletions src/openapi/content.rs
Original file line number Diff line number Diff line change
@@ -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<Value>,

/// Example for request body or response body.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg(not(feature = "serde_json"))]
pub example: Option<String>,
Expand All @@ -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<Value>,

#[cfg(not(feature = "serde_json"))]
example: Option<String>,
}

from!(Content ContentBuilder schema, example);

self
impl ContentBuilder {
new!(pub ContentBuilder);

/// Add schema.
pub fn schema<I: Into<Component>>(mut self, component: I) -> Self {
add_value!(self schema component.into())
}

#[cfg(not(feature = "serde_json"))]
pub fn with_example<S: Into<String>>(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<Value>) -> Self {
add_value!(self example example)
}

self
/// Add example of schema.
#[cfg(not(feature = "serde_json"))]
pub fn example<S: Into<String>>(mut self, example: Option<S>) -> Self {
add_value!(self example example.map(|example| example.into()))
}

build_fn!(pub Content schema, example);
}
48 changes: 38 additions & 10 deletions src/openapi/external_docs.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
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<String>,
}
}

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<S: AsRef<str>>(url: S) -> Self {
Self {
url: url.as_ref().to_string(),
..Default::default()
}
}
}

pub fn with_description<S: AsRef<str>>(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<I: Into<String>>(mut self, url: I) -> Self {
add_value!(self url url.into())
}

self
/// Add additional description of external documentation.
pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
add_value!(self description description.map(|description| description.into()))
}
}
65 changes: 51 additions & 14 deletions src/openapi/header.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
/// 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<String>,
}
}

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<C: Into<Component>>(component: C) -> Self {
Self {
schema: component.into(),
..Default::default()
}
}

pub fn with_description<S: AsRef<str>>(mut self, description: S) -> Self {
self.description = Some(description.as_ref().to_string());

self
}
}

impl Default for Header {
Expand All @@ -35,3 +60,15 @@ impl Default for Header {
}
}
}

impl HeaderBuilder {
/// Add schema of header.
pub fn schema<I: Into<Component>>(mut self, component: I) -> Self {
add_value!(self schema component.into())
}

/// Add additional description for header.
pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
add_value!(self description description.map(|description| description.into()))
}
}
4 changes: 2 additions & 2 deletions src/openapi/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,8 @@ impl Operation {
self
}

pub fn with_response<S: AsRef<str>>(mut self, code: S, response: Response) -> Self {
self.responses = self.responses.with_response(code, response);
pub fn with_response<S: Into<String>>(mut self, code: S, response: Response) -> Self {
self.responses.responses.insert(code.into(), response);

self
}
Expand Down
64 changes: 39 additions & 25 deletions src/openapi/request_body.rs
Original file line number Diff line number Diff line change
@@ -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<String>,

pub content: HashMap<String, Content>,

#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Required>,
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<String>,

/// Map of request body contents mapped by content type e.g. `application/json`.
pub content: HashMap<String, Content>,

/// Determines whether request body is reuqired in the request or not.
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Required>,
}
}

impl RequestBody {
/// Constrcut a new [`RequestBody`].
pub fn new() -> Self {
Default::default()
}
}

pub fn with_description<S: AsRef<str>>(mut self, description: S) -> Self {
self.description = Some(description.as_ref().to_string());

self
impl RequestBodyBuilder {
/// Add description for [`RequestBody`].
pub fn description<S: Into<String>>(mut self, description: Option<S>) -> 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<Required>) -> Self {
add_value!(self required required)
}

pub fn with_content<S: AsRef<str>>(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<S: Into<String>>(mut self, content_type: S, content: Content) -> Self {
self.content.insert(content_type.into(), content);

self
}
Expand Down
Loading