Skip to main content

nfw_core/openapi/
spec.rs

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}