1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct OpenApiSpec {
6 pub openapi: String,
7 pub info: InfoObject,
8 pub servers: Vec<ServerObject>,
9 pub paths: HashMap<String, PathItem>,
10 pub components: ComponentsObject,
11 #[serde(default)]
12 pub tags: Vec<TagObject>,
13}
14
15impl Default for OpenApiSpec {
16 fn default() -> Self {
17 Self {
18 openapi: "3.1.0".to_string(),
19 info: InfoObject::default(),
20 servers: vec![ServerObject {
21 url: "http://localhost:3000".to_string(),
22 description: Some("Development server".to_string()),
23 }],
24 paths: HashMap::new(),
25 components: ComponentsObject::default(),
26 tags: Vec::new(),
27 }
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct InfoObject {
33 pub title: String,
34 pub description: Option<String>,
35 pub version: String,
36 pub contact: Option<ContactObject>,
37 pub license: Option<LicenseObject>,
38}
39
40impl Default for InfoObject {
41 fn default() -> Self {
42 Self {
43 title: "NestForge Web API".to_string(),
44 description: Some("API documentation".to_string()),
45 version: "1.0.0".to_string(),
46 contact: None,
47 license: None,
48 }
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct ServerObject {
54 pub url: String,
55 pub description: Option<String>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ContactObject {
60 pub name: Option<String>,
61 pub email: Option<String>,
62 pub url: Option<String>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct LicenseObject {
67 pub name: String,
68 pub identifier: Option<String>,
69 pub url: Option<String>,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct TagObject {
74 pub name: String,
75 pub description: Option<String>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, Default)]
79pub struct PathItem {
80 #[serde(default)]
81 pub get: Option<OperationObject>,
82 #[serde(default)]
83 pub post: Option<OperationObject>,
84 #[serde(default)]
85 pub put: Option<OperationObject>,
86 #[serde(default)]
87 pub patch: Option<OperationObject>,
88 #[serde(default)]
89 pub delete: Option<OperationObject>,
90 #[serde(default)]
91 pub options: Option<OperationObject>,
92 #[serde(default)]
93 pub head: Option<OperationObject>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct OperationObject {
98 pub operation_id: Option<String>,
99 pub summary: Option<String>,
100 pub description: Option<String>,
101 pub tags: Vec<String>,
102 pub parameters: Vec<ParameterObject>,
103 pub request_body: Option<RequestBodyObject>,
104 pub responses: HashMap<String, ResponseObject>,
105 #[serde(default)]
106 pub deprecated: bool,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct ParameterObject {
111 pub name: String,
112 #[serde(rename = "in")]
113 pub location: ParameterLocation,
114 pub required: Option<bool>,
115 pub description: Option<String>,
116 pub schema: SchemaObject,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120#[serde(rename_all = "lowercase")]
121pub enum ParameterLocation {
122 Query,
123 Path,
124 Header,
125 Cookie,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct RequestBodyObject {
130 pub description: Option<String>,
131 pub required: bool,
132 pub content: HashMap<String, MediaTypeObject>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct ResponseObject {
137 pub description: String,
138 #[serde(default)]
139 pub content: HashMap<String, MediaTypeObject>,
140 #[serde(default)]
141 pub headers: HashMap<String, HeaderObject>,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct MediaTypeObject {
146 pub schema: Option<SchemaObject>,
147 #[serde(default)]
148 pub example: Option<serde_json::Value>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct HeaderObject {
153 pub description: Option<String>,
154 pub required: Option<bool>,
155 pub schema: SchemaObject,
156}
157
158#[derive(Debug, Clone, Serialize, Deserialize, Default)]
159pub struct SchemaObject {
160 #[serde(rename = "type")]
161 pub schema_type: Option<String>,
162 pub format: Option<String>,
163 pub properties: Option<HashMap<String, SchemaObject>>,
164 pub items: Option<Box<SchemaObject>>,
165 pub required: Option<Vec<String>>,
166 pub description: Option<String>,
167 pub example: Option<serde_json::Value>,
168 #[serde(default)]
169 pub nullable: bool,
170 #[serde(default)]
171 pub deprecated: bool,
172 pub enum_values: Option<Vec<serde_json::Value>>,
173 pub default: Option<serde_json::Value>,
174 pub minimum: Option<f64>,
175 pub maximum: Option<f64>,
176}
177
178impl SchemaObject {
179 pub fn string() -> Self {
180 Self {
181 schema_type: Some("string".to_string()),
182 format: None,
183 properties: None,
184 items: None,
185 required: None,
186 description: None,
187 example: None,
188 nullable: false,
189 deprecated: false,
190 enum_values: None,
191 default: None,
192 minimum: None,
193 maximum: None,
194 }
195 }
196
197 pub fn integer() -> Self {
198 Self {
199 schema_type: Some("integer".to_string()),
200 format: Some("int32".to_string()),
201 ..Default::default()
202 }
203 }
204
205 pub fn number() -> Self {
206 Self {
207 schema_type: Some("number".to_string()),
208 ..Default::default()
209 }
210 }
211
212 pub fn boolean() -> Self {
213 Self {
214 schema_type: Some("boolean".to_string()),
215 ..Default::default()
216 }
217 }
218
219 pub fn array(items: SchemaObject) -> Self {
220 Self {
221 schema_type: Some("array".to_string()),
222 items: Some(Box::new(items)),
223 ..Default::default()
224 }
225 }
226
227 pub fn object(properties: HashMap<String, SchemaObject>) -> Self {
228 Self {
229 schema_type: Some("object".to_string()),
230 properties: Some(properties),
231 ..Default::default()
232 }
233 }
234
235 pub fn with_example(mut self, example: serde_json::Value) -> Self {
236 self.example = Some(example);
237 self
238 }
239
240 pub fn with_description(mut self, description: &str) -> Self {
241 self.description = Some(description.to_string());
242 self
243 }
244
245 pub fn with_format(mut self, format: &str) -> Self {
246 self.format = Some(format.to_string());
247 self
248 }
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize, Default)]
252pub struct ComponentsObject {
253 pub schemas: HashMap<String, SchemaObject>,
254 #[serde(default)]
255 pub security_schemes: HashMap<String, SecuritySchemeObject>,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
259pub struct SecuritySchemeObject {
260 #[serde(rename = "type")]
261 pub scheme_type: String,
262 pub description: Option<String>,
263 pub name: Option<String>,
264 #[serde(rename = "in")]
265 pub location: Option<String>,
266 pub scheme: Option<String>,
267 pub bearer_format: Option<String>,
268}
269
270impl SecuritySchemeObject {
271 pub fn bearer() -> Self {
272 Self {
273 scheme_type: "http".to_string(),
274 description: Some("Bearer token authentication".to_string()),
275 name: None,
276 location: None,
277 scheme: Some("bearer".to_string()),
278 bearer_format: Some("JWT".to_string()),
279 }
280 }
281
282 pub fn api_key(name: &str, location: &str) -> Self {
283 Self {
284 scheme_type: "apiKey".to_string(),
285 description: Some("API key authentication".to_string()),
286 name: Some(name.to_string()),
287 location: Some(location.to_string()),
288 scheme: None,
289 bearer_format: None,
290 }
291 }
292}