Skip to main content

swagg/
lib.rs

1use openapiv3::OpenAPI;
2
3mod highway;
4mod printer;
5
6#[cfg(test)]
7pub mod test;
8
9use printer::Printable;
10
11/// Format for OpenAPI3 specification
12pub enum Format {
13    Yaml,
14    Json,
15}
16
17/// Describes convertation error
18#[derive(Debug)]
19pub enum Error {
20    InvalidSource,
21}
22
23impl std::fmt::Display for Error {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            Self::InvalidSource => write!(f, "OpenAPI structure cannot be parsed"),
27        }
28    }
29}
30
31impl std::error::Error for Error {}
32
33/// Convert source of OpenAPI3 specification to rust code in string representation
34pub fn to_string(source: &str, format: Format) -> Result<String, Error> {
35    let api: OpenAPI = match format {
36        Format::Yaml => serde_yaml::from_str(&source).map_err(|_| Error::InvalidSource)?,
37        Format::Json => serde_json::from_str(&source).map_err(|_| Error::InvalidSource)?,
38    };
39
40    // eprintln!("{:#?}", api.components);
41
42    let mut highway_components = highway::Components::new();
43
44    if let Some(components) = api.components {
45        // for (name, body) in components.request_bodies.iter() {
46        //     match body {
47        //         ReferenceOr::Item(body) => {
48        //             highway_components.parse_request_body(&name, &body);
49        //         }
50        //         ReferenceOr::Reference { reference } => {
51        //             log::info!("skipping request body reference {}", reference);
52        //         }
53        //     }
54        // }
55
56        for (name, schema) in components.schemas.iter() {
57            if let Err(reason) = highway_components.parse_schema(&name, &schema) {
58                eprintln!("Failed {} {:#?}", name, reason);
59            }
60        }
61    }
62
63    println!("{:#?}", highway_components);
64
65    let mut generated: printer::GeneratedModule = highway_components.into();
66
67    generated.api.set_name(api.info.title);
68    generated.api.set_description(api.info.description);
69    generated
70        .api
71        .set_terms_of_service(api.info.terms_of_service);
72
73    Ok(format!("{}", generated.print()))
74}
75
76#[cfg(test)]
77mod tests {
78    use super::{to_string, Format};
79    use crate::test::pretty;
80    use insta::assert_snapshot;
81
82    #[test]
83    fn yaml_schema_prints() {
84        let schema = r###"
85openapi: 3.0.1
86info:
87  title: Demo API.
88  version: 0.1.0
89  description: Test api
90paths:
91  "/stub":
92    get:
93      operationId: stub
94      responses:
95        303:
96          description: "Stub"
97
98components:
99  schemas:
100    SessionUser:
101      description: Current user in a session
102      type: object
103      required:
104        - firstName
105        - lastName
106      properties:
107        firstName:
108          type: string
109        lastName:
110          type: string
111        inner:
112          type: object
113          properties:
114            foo:
115              type: number
116            bar:
117              type: integer
118            baz:
119              type: object
120              properties:
121                demo:
122                  type: string
123          required:
124            - baz
125            - bar
126        "###;
127
128        assert_snapshot!(pretty(to_string(&schema, Format::Yaml).unwrap()), @r###"
129        #![allow(dead_code, unused_imports)]
130        pub mod api {
131            #[doc = "Test api"]
132            pub struct DemoApi {
133                api: actix_swagger::Api,
134            }
135            impl DemoApi {
136                pub fn new() -> Self {
137                    Self {
138                        api: actix_swagger::Api::new(),
139                    }
140                }
141            }
142            impl Default for DemoApi {
143                fn default() -> Self {
144                    let api = Self::new();
145                    api
146                }
147            }
148            impl actix_web::dev::HttpServiceFactory for DemoApi {
149                fn register(self, config: &mut actix_web::dev::AppService) {
150                    self.api.register(config);
151                }
152            }
153            use super::paths;
154            use actix_swagger::{Answer, Method};
155            use actix_web::{dev::Factory, FromRequest};
156            use std::future::Future;
157            impl DemoApi {}
158        }
159        pub mod components {
160            pub mod parameters {
161                use serde::{Deserialize, Serialize};
162            }
163            pub mod request_bodies {
164                use serde::{Deserialize, Serialize};
165            }
166            pub mod responses {
167                use serde::{Deserialize, Serialize};
168            }
169            pub mod schemas {
170                use serde::{Deserialize, Serialize};
171                #[doc = "Current user in a session"]
172                #[derive(Debug, Serialize, Deserialize)]
173                pub struct SessionUser {
174                    #[serde(rename = "firstName")]
175                    pub first_name: String,
176                    #[serde(rename = "lastName")]
177                    pub last_name: String,
178                    pub inner: Option<SessionUserInner>,
179                }
180                #[derive(Debug, Serialize, Deserialize)]
181                pub struct SessionUserInner {
182                    pub foo: Option<f32>,
183                    pub bar: i32,
184                    pub baz: SessionUserInnerBaz,
185                }
186                #[derive(Debug, Serialize, Deserialize)]
187                pub struct SessionUserInnerBaz {
188                    pub demo: Option<String>,
189                }
190            }
191        }
192        pub mod paths {
193            use super::components::{parameters, responses};
194        }
195        "###);
196    }
197}