br_addon/
swagger.rs

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    /// 组件 密钥认证设置
11    components: JsonValue,
12    /// 分类标签
13    tags: BTreeMap<String, Tag>,
14
15    security: Vec<JsonValue>,
16    /// 路径
17    paths: BTreeMap<String, BTreeMap<String, Api>>,
18}
19
20impl Swagger {
21    #[must_use] 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: BTreeMap::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/// 接口项目信息
164#[derive(Clone)]
165struct Info {
166    /// 项目标题
167    title: String,
168    /// 项目描述
169    description: String,
170    /// 项目版本
171    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/// 服务器地址
183#[derive(Clone, Debug)]
184pub struct Server {
185    url: String,
186    description: String,
187    variables: BTreeMap<String, JsonValue>,
188}
189impl Server {
190    #[must_use] 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/// 标签分类
208#[derive(Clone)]
209struct Tag {
210    /// 标签名称
211    name: String,
212    /// 标签描述
213    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    // parameters: JsonValue,
228    description: String,
229    /// 请求消息体
230    request_body: RequestBody,
231    /// 响应消息体
232    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            // parameters: object! {},
251        };
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            // parameters: object! {},
275        };
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_components(data: &mut JsonValue, key: &str, _value: JsonValue) {
368    //     data["$ref"] = format!("#/components/schemas/{}", key).into();
369    // }
370    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            "int" => "integer",
407            "switch" => "boolean",
408            "radio" => "string",
409            "select" | "array" => "array",
410            _ => name,
411        }
412        .into()
413    }
414    pub fn json(&self) -> JsonValue {
415        object! {
416                required: self.required,
417                content: self.content.clone()
418        }
419    }
420}