1use crate::action::Action;
2use json::{object, JsonValue};
3use std::collections::BTreeMap;
4
5#[derive(Clone)]
6pub struct Swagger {
7 openapi: String,
8 info: Info,
9 servers: Vec<Server>,
10 components: JsonValue,
12 tags: BTreeMap<String, Tag>,
14
15 security: Vec<JsonValue>,
16 paths: BTreeMap<String, BTreeMap<String, Api>>,
18}
19
20impl Swagger {
21 pub fn new(version: &str, title: &str, description: &str) -> Swagger {
22 Swagger {
23 openapi: "3.0.0".to_string(),
24 info: Info {
25 title: title.to_string(),
26 description: description.to_string(),
27 version: version.to_string(),
28 },
29 servers: vec![],
30 components: object! {},
31 tags: Default::default(),
32 security: vec![],
33 paths: Default::default(),
34 }
35 }
36 pub fn add_server(&mut self, url: &str, description: &str) -> Server {
37 Server {
38 url: url.to_string(),
39 description: description.to_string(),
40 variables: Default::default(),
41 }
42 }
43 pub fn set_server(&mut self, server: Server) {
44 self.servers.push(server);
45 }
46 pub fn add_header(&mut self, key: &str, description: &str, example: &str) {
47 self.components["parameters"]["GlobalHeader"] = object! {
48 "name":key,
49 "in": "header",
50 "description": description,
51 "required": true,
52 "schema": {
53 "type": "string",
54 "example":example
55 }
56 }
57 }
58 pub fn add_components_bearer_token(&mut self) {
59 self.components["securitySchemes"]["BearerToken"] = object! {
60 "type": "http",
61 "scheme": "bearer",
62 "bearerFormat": "Token"
63 };
64 self.security.push(object! {
65 "BearerToken":[]
66 })
67 }
68 pub fn add_components_header(&mut self, key: &str, description: &str, example: &str) {
69 self.components["securitySchemes"][key] = object! {
70 "type": "apiKey",
71 "in": "header",
72 "name": key,
73 "description": description,
74 };
75 let mut security = object! {};
76 security[key] = example.into();
77 self.security.push(security)
78 }
79 pub fn add_authorization_header(&mut self, token: &str) {
80 self.components["parameters"]["AuthorizationHeader"] = object! {
81 "name": "Authorization",
82 "in": "header",
83 "required": true,
84 "description": "Bearer token for authentication",
85 "schema":{
86 "type": "string",
87 "example":format!("Bearer {}",token)
88 }
89 };
90 }
91
92 pub fn set_global(&mut self, key: &str, example: &str, description: &str) {
93 self.components["schemas"][key]["type"] = "string".into();
94 self.components["schemas"][key]["description"] = description.into();
95 self.components["schemas"][key]["example"] = example.into();
96 }
97
98 pub fn add_tags(&mut self, name: &str, description: &str) {
99 self.tags.insert(
100 name.to_string(),
101 Tag {
102 name: name.to_string(),
103 description: description.to_string(),
104 },
105 );
106 }
107 pub fn add_paths(&mut self, mut action: Box<dyn Action>) {
108 let path = format!("/{}", action.api().replace(".", "/"));
109 let mut t = BTreeMap::new();
110 t.insert(
111 action.method().str().to_lowercase().clone(),
112 Api::new(action, self.components.clone()),
113 );
114 self.paths.insert(path, t);
115 }
116 pub fn add_path(&mut self, mut action: Box<dyn Action>) {
117 let path = format!("/{}", action.path());
118 if !self.paths.contains_key(&path) {
119 let mut t = BTreeMap::new();
120 t.insert(
121 action.method().str().to_lowercase().clone(),
122 Api::new(action, self.components.clone()),
123 );
124 self.paths.insert(path, t);
125 }else {
126 let mut t=self.paths.get(&path).unwrap().clone();
127 t.insert(
128 action.method().str().to_lowercase().clone(),
129 Api::new(action, self.components.clone()),
130 );
131 self.paths.insert(path, t);
132 }
133 }
134 pub fn add_tag_paths(&mut self, tag: &str, mut action: Box<dyn Action>) {
135 let path = format!("/{tag}/{}", action.api().replace(".", "/"));
136 let mut t = BTreeMap::new();
137 t.insert(
138 action.method().str().to_lowercase().clone(),
139 Api::new_tag(tag, action, self.components.clone()),
140 );
141 self.paths.insert(path, t);
142 }
143 pub fn json(&mut self) -> JsonValue {
144 let mut paths = BTreeMap::new();
145 for (key, value) in self.paths.iter_mut() {
146 let mut t = BTreeMap::new();
147 for (x, y) in value.iter_mut() {
148 t.insert(x.clone(), y.json().clone());
149 }
150 paths.insert(key.clone(), t.clone());
151 }
152 object! {
153 openapi: self.openapi.clone(),
154 info: self.info.json(),
155 servers: self.servers.iter().map(|x|x.json()).collect::<Vec<JsonValue>>().clone(),
156 components: self.components.clone(),
157 security:self.security.clone(),
158 tags: self.tags.values().map(|y| y.json()).collect::<Vec<JsonValue>>().clone(),
159 paths: paths.clone()
160 }
161 }
162}
163#[derive(Clone)]
165struct Info {
166 title: String,
168 description: String,
170 version: String,
172}
173impl Info {
174 pub fn json(&self) -> JsonValue {
175 object! {
176 title: self.title.clone(),
177 description: self.description.clone(),
178 version: self.version.clone()
179 }
180 }
181}
182#[derive(Clone, Debug)]
184pub struct Server {
185 url: String,
186 description: String,
187 variables: BTreeMap<String, JsonValue>,
188}
189impl Server {
190 pub fn json(&self) -> JsonValue {
191 object! {
192 url: self.url.clone(),
193 description: self.description.clone(),
194 variables: self.variables.clone()
195 }
196 }
197 pub fn set_variable(&mut self, key: &str, value: JsonValue, description: &str) {
198 self.variables.insert(
199 key.to_string(),
200 object! {
201 default:value,
202 description:description
203 },
204 );
205 }
206}
207#[derive(Clone)]
209struct Tag {
210 name: String,
212 description: String,
214}
215impl Tag {
216 pub fn json(&self) -> JsonValue {
217 object! {
218 name: self.name.clone(),
219 description: self.description.clone(),
220 }
221 }
222}
223#[derive(Clone)]
224struct Api {
225 tags: Vec<String>,
226 summary: String,
227 description: String,
229 request_body: RequestBody,
231 responses: JsonValue,
233}
234
235impl Api {
236 pub fn new_tag(tag: &str, mut action: Box<dyn Action>, components: JsonValue) -> Api {
237 let apis = action.api();
238 let binding = apis.clone();
239 let mut apis = binding.split(".");
240 let mut t = Self {
241 tags: vec![format!(
242 "{tag}.{}.{}",
243 apis.next().unwrap().to_string(),
244 apis.next().unwrap().to_string()
245 )],
246 summary: action.title().to_string(),
247 description: action.description().to_string(),
248 request_body: RequestBody::new(components.clone()),
249 responses: object! {},
250 };
252
253 if !action.params().is_empty() {
254 t.request_body.set_required(true);
255 t.request_body
256 .set_content(action.content_type().str().as_str(), action.params());
257 }
258 t
259 }
260 pub fn new(mut action: Box<dyn Action>, components: JsonValue) -> Api {
261 let apis = action.api();
262 let binding = apis.clone();
263 let mut apis = binding.split(".");
264 let mut t = Self {
265 tags: vec![format!(
266 "{}.{}",
267 apis.next().unwrap().to_string(),
268 apis.next().unwrap().to_string()
269 )],
270 summary: action.title().to_string(),
271 description: action.description().to_string(),
272 request_body: RequestBody::new(components.clone()),
273 responses: object! {},
274 };
276
277 if !action.params().is_empty() {
278 t.request_body.set_required(true);
279 t.request_body
280 .set_content(action.content_type().str().as_str(), action.params());
281 }
282 t
283 }
284 pub fn json(&self) -> JsonValue {
285 let mut t = object! {
286 tags:self.tags.clone(),
287 summary: self.summary.clone(),
288 description: self.description.clone(),
289 requestBody: self.request_body.json(),
290 responses: self.responses.clone(),
291 };
292 if !t["requestBody"]["required"].as_bool().unwrap() {
293 t.remove("requestBody");
294 }
295 t
296 }
297}
298
299#[derive(Clone)]
300struct RequestBody {
301 required: bool,
302 content: JsonValue,
303 components: JsonValue,
304}
305impl RequestBody {
306 pub fn new(components: JsonValue) -> RequestBody {
307 Self {
308 required: false,
309 content: object! {},
310 components,
311 }
312 }
313 pub fn set_required(&mut self, state: bool) {
314 self.required = state;
315 }
316 pub fn set_content(&mut self, content_type: &str, params: JsonValue) {
317 self.content[content_type] = object! {
318 schema:object! {"type":if params.is_array() {"array"}else{"object"}}
319 };
320 match self.content[content_type]["schema"]["type"]
321 .as_str()
322 .unwrap_or("")
323 {
324 "object" => self
325 .clone()
326 .set_schema_object(&mut self.content[content_type]["schema"], params.clone()),
327 "array" => RequestBody::set_schema_array(
328 &mut self.content[content_type]["schema"],
329 params.clone(),
330 ),
331 _ => {}
332 }
333 for (field, data) in params.entries() {
334 if self.clone().components["schemas"][field].is_empty() {
335 self.content[content_type]["example"][field] = data["example"].clone();
336 } else {
337 self.content[content_type]["example"][field] =
338 self.clone().components["schemas"][field]["example"].clone();
339 }
340 }
341 }
342 fn set_schema_object(self, data: &mut JsonValue, params: JsonValue) {
343 for (key, value) in params.entries() {
344 data["properties"][key]["type"] =
345 RequestBody::mode(value["mode"].as_str().unwrap_or(""));
346 match value["mode"].as_str().unwrap_or("") {
347 "radio" | "select" => {
348 data["properties"][key]["enum"] = value["option"].clone();
349 }
350 _ => {}
351 }
352 match data["properties"][key]["type"].as_str().unwrap_or("") {
353 "object" => self
354 .clone()
355 .set_sub_object(&mut data["properties"][key], value["items"].clone()),
356 "array" => {
357 data["properties"][key]["example"] = value["example"].clone();
358 data["properties"][key]["default"] = value["example"].clone();
359 RequestBody::set_sub_array(&mut data["properties"][key], value["items"].clone())
360 }
361 _ => {
362 RequestBody::set_schema_str(&mut data["properties"][key], value.clone());
363 }
364 }
365 }
366 }
367 fn set_schema_str(data: &mut JsonValue, params: JsonValue) {
371 data["type"] = RequestBody::mode(params["mode"].as_str().unwrap_or(""));
372 data["example"] = params["example"].clone();
373 data["default"] = params["example"].clone();
374 }
375 fn set_schema_array(data: &mut JsonValue, params: JsonValue) {
376 data["items"] = params;
377 }
378 fn set_sub_array(data: &mut JsonValue, params: JsonValue) {
379 data["items"]["type"] = params["mode"].as_str().unwrap_or("string").into();
380 }
381 fn set_sub_object(self, data: &mut JsonValue, params: JsonValue) {
382 for (key, value) in params.entries() {
383 data["properties"][key]["type"] =
384 RequestBody::mode(value["mode"].as_str().unwrap_or(""));
385 match value["mode"].as_str().unwrap_or("") {
386 "radio" | "select" => {
387 data["properties"][key]["enum"] = value["option"].clone();
388 }
389 _ => {}
390 }
391 match data["properties"][key]["type"].as_str().unwrap_or("") {
392 "object" => self
393 .clone()
394 .set_sub_object(&mut data["properties"][key], value["items"].clone()),
395 "array" => {
396 RequestBody::set_sub_array(&mut data["properties"][key], value["items"].clone())
397 }
398 _ => {
399 RequestBody::set_schema_str(&mut data["properties"][key], value.clone());
400 }
401 }
402 }
403 }
404 fn mode(name: &str) -> JsonValue {
405 match name {
406 "array" => "array",
407 "int" => "integer",
408 "switch" => "boolean",
409 "radio" => "string",
410 "select" => "array",
411 _ => name,
412 }
413 .into()
414 }
415 pub fn json(&self) -> JsonValue {
416 object! {
417 required: self.required,
418 content: self.content.clone()
419 }
420 }
421}