utoipauto_core/
attribute_utils.rs

1use quote::ToTokens;
2use syn::Attribute;
3
4pub fn update_openapi_macro_attributes(
5    macro_attibutes: &mut Vec<Attribute>,
6    uto_paths: &String,
7    uto_models: &String,
8    uto_responses: &String,
9) {
10    let mut is_ok = false;
11    for attr in macro_attibutes {
12        if !attr.path().is_ident("openapi") {
13            continue;
14        }
15        is_ok = true;
16        let mut src_uto_macro = attr.to_token_stream().to_string();
17
18        src_uto_macro = src_uto_macro.replace("#[openapi()]", "");
19        src_uto_macro = src_uto_macro.replace("#[openapi(", "");
20        src_uto_macro = src_uto_macro.replace(")]", "");
21        *attr = build_new_openapi_attributes(src_uto_macro, uto_paths, uto_models, uto_responses);
22    }
23    if !is_ok {
24        panic!("No utoipa::openapi Macro found !");
25    }
26}
27
28/// Build the new openapi macro attribute with the newly discovered paths
29pub fn build_new_openapi_attributes(
30    src_uto_macro: String,
31    uto_paths: &String,
32    uto_models: &String,
33    uto_responses: &String,
34) -> Attribute {
35    let paths = extract_paths(src_uto_macro.clone());
36    let schemas = extract_schemas(src_uto_macro.clone());
37    let responses = extract_responses(src_uto_macro.clone());
38    let src_uto_macro = remove_paths(src_uto_macro);
39    let src_uto_macro = remove_schemas(src_uto_macro);
40    let src_uto_macro = remove_responses(src_uto_macro);
41    let src_uto_macro = remove_components(src_uto_macro);
42
43    let paths = format!("{}{}", uto_paths, paths);
44    let schemas = format!("{}{}", uto_models, schemas);
45    let responses = format!("{}{}", uto_responses, responses);
46    let src_uto_macro = format!(
47        "paths({}),components(schemas({}),responses({})),{}",
48        paths, schemas, responses, src_uto_macro
49    )
50    .replace(",,", ",");
51
52    let stream: proc_macro2::TokenStream = src_uto_macro.parse().unwrap();
53
54    syn::parse_quote! { #[openapi( #stream )] }
55}
56
57fn remove_paths(src_uto_macro: String) -> String {
58    if src_uto_macro.contains("paths(") {
59        let paths = src_uto_macro.split("paths(").collect::<Vec<&str>>()[1];
60        let paths = paths.split(')').collect::<Vec<&str>>()[0];
61        src_uto_macro
62            .replace(format!("paths({})", paths).as_str(), "")
63            .replace(",,", ",")
64    } else {
65        src_uto_macro
66    }
67}
68
69fn remove_schemas(src_uto_macro: String) -> String {
70    if src_uto_macro.contains("schemas(") {
71        let schemas = src_uto_macro.split("schemas(").collect::<Vec<&str>>()[1];
72        let schemas = schemas.split(')').collect::<Vec<&str>>()[0];
73        src_uto_macro
74            .replace(format!("schemas({})", schemas).as_str(), "")
75            .replace(",,", ",")
76    } else {
77        src_uto_macro
78    }
79}
80
81fn remove_components(src_uto_macro: String) -> String {
82    if src_uto_macro.contains("components(") {
83        let components = src_uto_macro.split("components(").collect::<Vec<&str>>()[1];
84        let components = components.split(')').collect::<Vec<&str>>()[0];
85        src_uto_macro
86            .replace(format!("components({})", components).as_str(), "")
87            .replace(",,", ",")
88    } else {
89        src_uto_macro
90    }
91}
92
93fn remove_responses(src_uto_macro: String) -> String {
94    if src_uto_macro.contains("responses(") {
95        let responses = src_uto_macro.split("responses(").collect::<Vec<&str>>()[1];
96        let responses = responses.split(')').collect::<Vec<&str>>()[0];
97        src_uto_macro
98            .replace(format!("responses({})", responses).as_str(), "")
99            .replace(",,", ",")
100    } else {
101        src_uto_macro
102    }
103}
104
105fn extract_paths(src_uto_macro: String) -> String {
106    if src_uto_macro.contains("paths(") {
107        let paths = src_uto_macro.split("paths(").collect::<Vec<&str>>()[1];
108        let paths = paths.split(')').collect::<Vec<&str>>()[0];
109        paths.to_string()
110    } else {
111        "".to_string()
112    }
113}
114
115fn extract_schemas(src_uto_macro: String) -> String {
116    if src_uto_macro.contains("schemas(") {
117        let schemas = src_uto_macro.split("schemas(").collect::<Vec<&str>>()[1];
118        let schemas = schemas.split(')').collect::<Vec<&str>>()[0];
119        schemas.to_string()
120    } else {
121        "".to_string()
122    }
123}
124
125fn extract_responses(src_uto_macro: String) -> String {
126    if src_uto_macro.contains("responses(") {
127        let responses = src_uto_macro.split("responses(").collect::<Vec<&str>>()[1];
128        let responses = responses.split(')').collect::<Vec<&str>>()[0];
129        responses.to_string()
130    } else {
131        "".to_string()
132    }
133}
134#[cfg(test)]
135mod test {
136    use quote::ToTokens;
137
138    #[test]
139    fn test_remove_paths() {
140        assert_eq!(
141            super::remove_paths("description(test),paths(p1),info(test)".to_string()),
142            "description(test),info(test)".to_string()
143        );
144    }
145
146    #[test]
147    fn test_extract_paths() {
148        assert_eq!(super::extract_paths("paths(p1)".to_string()), "p1".to_string());
149    }
150
151    #[test]
152    fn test_extract_paths_empty() {
153        assert_eq!(super::extract_paths("".to_string()), "".to_string());
154    }
155
156    #[test]
157    fn test_build_new_openapi_attributes() {
158        assert_eq!(
159            super::build_new_openapi_attributes(
160                "".to_string(),
161                &"./src".to_string(),
162                &"".to_string(),
163                &"".to_string(),
164            )
165                .to_token_stream()
166                .to_string()
167                .replace(' ', ""),
168            "#[openapi(paths(./src),components(schemas(),responses()),)]".to_string()
169        );
170    }
171
172    #[test]
173    fn test_build_new_openapi_attributes_path_replace() {
174        assert_eq!(
175            super::build_new_openapi_attributes(
176                "paths(p1)".to_string(),
177                &"./src,".to_string(),
178                &"".to_string(),
179                &"".to_string(),
180            )
181            .to_token_stream()
182            .to_string()
183            .replace(' ', ""),
184            "#[openapi(paths(./src,p1),components(schemas(),responses()),)]".to_string()
185        );
186    }
187
188    #[test]
189    fn test_build_new_openapi_attributes_components() {
190        assert_eq!(
191            super::build_new_openapi_attributes(
192                "paths(p1)".to_string(),
193                &"./src,".to_string(),
194                &"model".to_string(),
195                &"".to_string()
196            )
197            .to_token_stream()
198            .to_string()
199            .replace(' ', ""),
200            "#[openapi(paths(./src,p1),components(schemas(model),responses()),)]".to_string()
201        );
202    }
203
204    #[test]
205    fn test_build_new_openapi_attributes_components_schemas_replace() {
206        assert_eq!(
207            super::build_new_openapi_attributes(
208                "paths(p1), components(schemas(m1))".to_string(),
209                &"./src,".to_string(),
210                &"model,".to_string(),
211                &"".to_string(),
212            )
213            .to_token_stream()
214            .to_string()
215            .replace(' ', ""),
216            "#[openapi(paths(./src,p1),components(schemas(model,m1),responses()),)]".to_string()
217        );
218    }
219
220    #[test]
221    fn test_build_new_openapi_attributes_components_responses_replace() {
222        assert_eq!(
223            super::build_new_openapi_attributes(
224                "paths(p1), components(responses(r1))".to_string(),
225                &"./src,".to_string(),
226                &"".to_string(),
227                &"response,".to_string(),
228            )
229            .to_token_stream()
230            .to_string()
231            .replace(' ', ""),
232            "#[openapi(paths(./src,p1),components(schemas(),responses(response,r1)),)]".to_string()
233        );
234    }
235
236    #[test]
237    fn test_build_new_openapi_attributes_components_responses_schemas_replace() {
238        assert_eq!(
239            super::build_new_openapi_attributes(
240                "paths(p1), components(responses(r1), schemas(m1))".to_string(),
241                &"./src,".to_string(),
242                &"model,".to_string(),
243                &"response,".to_string(),
244            )
245            .to_token_stream()
246            .to_string()
247            .replace(' ', ""),
248            "#[openapi(paths(./src,p1),components(schemas(model,m1),responses(response,r1)),)]".to_string()
249        );
250    }
251
252    #[test]
253    fn test_build_new_openapi_attributes_components_responses_schemas() {
254        assert_eq!(
255            super::build_new_openapi_attributes(
256                "paths(p1), components(responses(r1), schemas(m1))".to_string(),
257                &"./src,".to_string(),
258                &"".to_string(),
259                &"response,".to_string(),
260            )
261            .to_token_stream()
262            .to_string()
263            .replace(' ', ""),
264            "#[openapi(paths(./src,p1),components(schemas(m1),responses(response,r1)),)]".to_string()
265        );
266    }
267
268    #[test]
269    fn test_build_new_openapi_attributes_components_schemas_reponses() {
270        assert_eq!(
271            super::build_new_openapi_attributes(
272                "paths(p1), components(schemas(m1), responses(r1))".to_string(),
273                &"./src,".to_string(),
274                &"model,".to_string(),
275                &"".to_string(),
276            )
277            .to_token_stream()
278            .to_string()
279            .replace(' ', ""),
280            "#[openapi(paths(./src,p1),components(schemas(model,m1),responses(r1)),)]".to_string()
281        );
282    }
283}