Skip to main content

spikard_codegen/openapi/
spec.rs

1//! `OpenAPI` 3.1 specification data structures
2
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7/// `OpenAPI` 3.1 Specification
8#[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}