1use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct OpenApiSpec {
10 pub openapi: String,
11 pub info: Info,
12
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub servers: Option<Vec<Server>>,
15
16 pub paths: IndexMap<String, PathItem>,
17
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub components: Option<Components>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub tags: Option<Vec<Tag>>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct Info {
27 pub title: String,
28 pub version: String,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub description: Option<String>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Server {
36 pub url: String,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub description: Option<String>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct PathItem {
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub get: Option<Operation>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub post: Option<Operation>,
49
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub put: Option<Operation>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub patch: Option<Operation>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub delete: Option<Operation>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub parameters: Option<Vec<Parameter>>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Operation {
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub summary: Option<String>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub description: Option<String>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub operation_id: Option<String>,
73
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub parameters: Option<Vec<Parameter>>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub request_body: Option<RequestBody>,
79
80 pub responses: IndexMap<String, Response>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub tags: Option<Vec<String>>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Parameter {
88 pub name: String,
89
90 #[serde(rename = "in")]
91 pub location: String,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub description: Option<String>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub required: Option<bool>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub schema: Option<Schema>,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct RequestBody {
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub description: Option<String>,
107
108 pub content: IndexMap<String, MediaType>,
109
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub required: Option<bool>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct Response {
116 pub description: String,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub content: Option<IndexMap<String, MediaType>>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub headers: Option<IndexMap<String, Header>>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct MediaType {
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub schema: Option<Schema>,
129
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub example: Option<Value>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub examples: Option<IndexMap<String, Example>>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Example {
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub summary: Option<String>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub value: Option<Value>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct Header {
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub description: Option<String>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub schema: Option<Schema>,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(untagged)]
157pub enum Schema {
158 Ref {
159 #[serde(rename = "$ref")]
160 reference: String,
161 },
162 Object(Box<SchemaObject>),
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct SchemaObject {
167 #[serde(rename = "type")]
168 pub schema_type: String,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub properties: Option<IndexMap<String, Box<Schema>>>,
172
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub required: Option<Vec<String>>,
175
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub format: Option<String>,
178
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub items: Option<Box<Schema>>,
181
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub minimum: Option<f64>,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub maximum: Option<f64>,
187
188 #[serde(skip_serializing_if = "Option::is_none")]
189 #[serde(rename = "minLength")]
190 pub min_length: Option<usize>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
193 #[serde(rename = "maxLength")]
194 pub max_length: Option<usize>,
195
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub pattern: Option<String>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub description: Option<String>,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct Components {
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub schemas: Option<IndexMap<String, Schema>>,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub responses: Option<IndexMap<String, Response>>,
210
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub parameters: Option<IndexMap<String, Parameter>>,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct Tag {
217 pub name: String,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub description: Option<String>,
221}
222
223impl OpenApiSpec {
224 pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
225 Self {
226 openapi: "3.1.0".to_string(),
227 info: Info {
228 title: title.into(),
229 version: version.into(),
230 description: None,
231 },
232 servers: None,
233 paths: IndexMap::new(),
234 components: Some(Components {
235 schemas: Some(IndexMap::new()),
236 responses: None,
237 parameters: None,
238 }),
239 tags: None,
240 }
241 }
242}