utoipa/openapi/info.rs
1//! Implements [OpenAPI Metadata][info] types.
2//!
3//! Refer to [`OpenApi`][openapi_trait] trait and [derive documentation][derive]
4//! for examples and usage details.
5//!
6//! [info]: <https://spec.openapis.org/oas/latest.html#info-object>
7//! [openapi_trait]: ../../trait.OpenApi.html
8//! [derive]: ../../derive.OpenApi.html
9use serde::{Deserialize, Serialize};
10
11use super::{builder, set_value};
12
13builder! {
14 /// # Examples
15 ///
16 /// Create [`Info`] using [`InfoBuilder`].
17 /// ```rust
18 /// # use utoipa::openapi::{Info, InfoBuilder, ContactBuilder};
19 /// let info = InfoBuilder::new()
20 /// .title("My api")
21 /// .version("1.0.0")
22 /// .contact(Some(ContactBuilder::new()
23 /// .name(Some("Admin Admin"))
24 /// .email(Some("amdin@petapi.com"))
25 /// .build()
26 /// ))
27 /// .build();
28 /// ```
29 InfoBuilder;
30
31 /// OpenAPI [Info][info] object represents metadata of the API.
32 ///
33 /// You can use [`Info::new`] to construct a new [`Info`] object or alternatively use [`InfoBuilder::new`]
34 /// to construct a new [`Info`] with chainable configuration methods.
35 ///
36 /// [info]: <https://spec.openapis.org/oas/latest.html#info-object>
37 #[non_exhaustive]
38 #[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
39 #[cfg_attr(feature = "debug", derive(Debug))]
40 #[serde(rename_all = "camelCase")]
41 pub struct Info {
42 /// Title of the API.
43 pub title: String,
44
45 /// Optional description of the API.
46 ///
47 /// Value supports markdown syntax.
48 #[serde(skip_serializing_if = "Option::is_none")]
49 pub description: Option<String>,
50
51 /// Optional url for terms of service.
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub terms_of_service: Option<String>,
54
55 /// Contact information of exposed API.
56 ///
57 /// See more details at: <https://spec.openapis.org/oas/latest.html#contact-object>.
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub contact: Option<Contact>,
60
61 /// License of the API.
62 ///
63 /// See more details at: <https://spec.openapis.org/oas/latest.html#license-object>.
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub license: Option<License>,
66
67 /// Document version typically the API version.
68 pub version: String,
69 }
70}
71
72impl Info {
73 /// Construct a new [`Info`] object.
74 ///
75 /// This function accepts two arguments. One which is the title of the API and two the
76 /// version of the api document typically the API version.
77 ///
78 /// # Examples
79 ///
80 /// ```rust
81 /// # use utoipa::openapi::Info;
82 /// let info = Info::new("Pet api", "1.1.0");
83 /// ```
84 pub fn new<S: Into<String>>(title: S, version: S) -> Self {
85 Self {
86 title: title.into(),
87 version: version.into(),
88 ..Default::default()
89 }
90 }
91}
92
93impl InfoBuilder {
94 /// Add title of the API.
95 pub fn title<I: Into<String>>(mut self, title: I) -> Self {
96 set_value!(self title title.into())
97 }
98
99 /// Add version of the api document typically the API version.
100 pub fn version<I: Into<String>>(mut self, version: I) -> Self {
101 set_value!(self version version.into())
102 }
103
104 /// Add description of the API.
105 pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
106 set_value!(self description description.map(|description| description.into()))
107 }
108
109 /// Add url for terms of the API.
110 pub fn terms_of_service<S: Into<String>>(mut self, terms_of_service: Option<S>) -> Self {
111 set_value!(self terms_of_service terms_of_service.map(|terms_of_service| terms_of_service.into()))
112 }
113
114 /// Add contact information of the API.
115 pub fn contact(mut self, contact: Option<Contact>) -> Self {
116 set_value!(self contact contact)
117 }
118
119 /// Add license of the API.
120 pub fn license(mut self, license: Option<License>) -> Self {
121 set_value!(self license license)
122 }
123}
124
125builder! {
126 /// See the [`InfoBuilder`] for combined usage example.
127 ContactBuilder;
128
129 /// OpenAPI [Contact][contact] information of the API.
130 ///
131 /// You can use [`Contact::new`] to construct a new [`Contact`] object or alternatively
132 /// use [`ContactBuilder::new`] to construct a new [`Contact`] with chainable configuration methods.
133 ///
134 /// [contact]: <https://spec.openapis.org/oas/latest.html#contact-object>
135 #[non_exhaustive]
136 #[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
137 #[cfg_attr(feature = "debug", derive(Debug))]
138 #[serde(rename_all = "camelCase")]
139 pub struct Contact {
140 /// Identifying name of the contact person or organization of the API.
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub name: Option<String>,
143
144 /// Url pointing to contact information of the API.
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub url: Option<String>,
147
148 /// Email of the contact person or the organization of the API.
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub email: Option<String>,
151 }
152}
153
154impl Contact {
155 /// Construct a new [`Contact`].
156 pub fn new() -> Self {
157 Default::default()
158 }
159}
160
161impl ContactBuilder {
162 /// Add name contact person or organization of the API.
163 pub fn name<S: Into<String>>(mut self, name: Option<S>) -> Self {
164 set_value!(self name name.map(|name| name.into()))
165 }
166
167 /// Add url pointing to the contact information of the API.
168 pub fn url<S: Into<String>>(mut self, url: Option<S>) -> Self {
169 set_value!(self url url.map(|url| url.into()))
170 }
171
172 /// Add email of the contact person or organization of the API.
173 pub fn email<S: Into<String>>(mut self, email: Option<S>) -> Self {
174 set_value!(self email email.map(|email| email.into()))
175 }
176}
177
178builder! {
179 LicenseBuilder;
180
181 /// OpenAPI [License][license] information of the API.
182 ///
183 /// [license]: <https://spec.openapis.org/oas/latest.html#license-object>
184 #[non_exhaustive]
185 #[derive(Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
186 #[cfg_attr(feature = "debug", derive(Debug))]
187 #[serde(rename_all = "camelCase")]
188 pub struct License {
189 /// Name of the license used e.g MIT or Apache-2.0
190 pub name: String,
191
192 /// Optional url pointing to the license.
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub url: Option<String>,
195 }
196}
197
198impl License {
199 /// Construct a new [`License`] object.
200 ///
201 /// Function takes name of the license as an argument e.g MIT.
202 pub fn new<S: Into<String>>(name: S) -> Self {
203 Self {
204 name: name.into(),
205 ..Default::default()
206 }
207 }
208}
209
210impl LicenseBuilder {
211 /// Add name of the license used in API.
212 pub fn name<S: Into<String>>(mut self, name: S) -> Self {
213 set_value!(self name name.into())
214 }
215
216 /// Add url pointing to the license used in API.
217 pub fn url<S: Into<String>>(mut self, url: Option<S>) -> Self {
218 set_value!(self url url.map(|url| url.into()))
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::Contact;
225
226 #[test]
227 fn contact_new() {
228 let contact = Contact::new();
229
230 assert!(contact.name.is_none());
231 assert!(contact.url.is_none());
232 assert!(contact.email.is_none());
233 }
234}