1use serde::{Deserialize, Serialize};
4
5mod components;
6mod paths;
7mod security;
8
9pub use components::*;
10pub use paths::*;
11pub use security::*;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct OpenApiInfo {
16 pub title: String,
18 pub version: String,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub description: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub terms_of_service: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub contact: Option<Contact>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub license: Option<License>,
32}
33
34impl Default for OpenApiInfo {
35 fn default() -> Self {
37 Self {
38 title: "API".to_string(),
39 version: "1.0.0".to_string(),
40 description: None,
41 terms_of_service: None,
42 contact: None,
43 license: None,
44 }
45 }
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Contact {
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub name: Option<String>,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub email: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub url: Option<String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct License {
65 pub name: String,
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub url: Option<String>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct ExternalDocumentation {
75 pub url: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub description: Option<String>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct Tag {
85 pub name: String,
87 #[serde(skip_serializing_if = "Option::is_none")]
89 pub description: Option<String>,
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub external_docs: Option<ExternalDocumentation>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct Server {
98 pub url: String,
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub description: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub variables: Option<std::collections::BTreeMap<String, ServerVariable>>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ServerVariable {
111 pub default: String,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub enum_values: Option<Vec<String>>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub description: Option<String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct OpenApiDoc {
124 pub openapi: String,
126 pub info: OpenApiInfo,
128 pub paths: std::collections::BTreeMap<String, PathItem>,
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub components: Option<Components>,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub servers: Option<Vec<Server>>,
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub tags: Option<Vec<Tag>>,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub external_docs: Option<ExternalDocumentation>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub security: Option<Vec<SecurityRequirement>>,
145}
146
147impl Default for OpenApiDoc {
148 fn default() -> Self {
150 Self {
151 openapi: "3.1.0".to_string(),
152 info: OpenApiInfo::default(),
153 paths: std::collections::BTreeMap::new(),
154 components: None,
155 servers: None,
156 tags: None,
157 external_docs: None,
158 security: None,
159 }
160 }
161}
162
163impl OpenApiDoc {
164 pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
166 Self { info: OpenApiInfo { title: title.into(), version: version.into(), ..Default::default() }, ..Default::default() }
167 }
168
169 pub fn description(mut self, desc: impl Into<String>) -> Self {
171 self.info.description = Some(desc.into());
172 self
173 }
174
175 pub fn path(mut self, path: impl Into<String>, item: PathItem) -> Self {
177 self.paths.insert(path.into(), item);
178 self
179 }
180
181 pub fn schema(mut self, name: impl Into<String>, schema: crate::Schema) -> Self {
183 let components = self.components.get_or_insert_with(Components::default);
184 components.schemas.insert(name.into(), schema);
185 self
186 }
187
188 pub fn security_scheme(mut self, name: impl Into<String>, scheme: SecurityScheme) -> Self {
190 let components = self.components.get_or_insert_with(Components::default);
191 components.security_schemes.insert(name.into(), scheme);
192 self
193 }
194
195 pub fn response(mut self, name: impl Into<String>, response: Response) -> Self {
197 let components = self.components.get_or_insert_with(Components::default);
198 components.responses.insert(name.into(), response);
199 self
200 }
201
202 pub fn parameter(mut self, name: impl Into<String>, parameter: Parameter) -> Self {
204 let components = self.components.get_or_insert_with(Components::default);
205 components.parameters.insert(name.into(), parameter);
206 self
207 }
208
209 pub fn request_body(mut self, name: impl Into<String>, body: RequestBody) -> Self {
211 let components = self.components.get_or_insert_with(Components::default);
212 components.request_bodies.insert(name.into(), body);
213 self
214 }
215
216 pub fn server(mut self, url: impl Into<String>, description: Option<String>) -> Self {
218 let servers = self.servers.get_or_insert_with(Vec::new);
219 servers.push(Server { url: url.into(), description, variables: None });
220 self
221 }
222
223 pub fn tag(mut self, name: impl Into<String>, description: Option<String>) -> Self {
225 let tags = self.tags.get_or_insert_with(Vec::new);
226 tags.push(Tag { name: name.into(), description, external_docs: None });
227 self
228 }
229
230 pub fn external_docs(mut self, url: impl Into<String>, description: Option<String>) -> Self {
232 self.external_docs = Some(ExternalDocumentation { url: url.into(), description });
233 self
234 }
235
236 pub fn security(mut self, security: SecurityRequirement) -> Self {
238 let securities = self.security.get_or_insert_with(Vec::new);
239 securities.push(security);
240 self
241 }
242
243 pub fn to_json(&self) -> serde_json::Value {
245 serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
246 }
247
248 #[cfg(feature = "yaml")]
250 pub fn to_yaml(&self) -> String {
251 serde_yaml::to_string(self).unwrap_or_default()
252 }
253}