1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#![cfg(feature = "actix-web")]

use actix_web::{
    dev::HttpServiceFactory, guard::Get, web, web::Data, HttpResponse, Resource,
    Responder as ActixResponder,
};

use crate::{ApiDoc, Config, SwaggerUi};

impl HttpServiceFactory for SwaggerUi {
    fn register(self, config: &mut actix_web::dev::AppService) {
        let mut urls = self
            .urls
            .into_iter()
            .map(|(url, openapi)| {
                register_api_doc_url_resource(url.url.as_ref(), ApiDoc::Utoipa(openapi), config);
                url
            })
            .collect::<Vec<_>>();
        let external_api_docs = self.external_urls.into_iter().map(|(url, api_doc)| {
            register_api_doc_url_resource(url.url.as_ref(), ApiDoc::Value(api_doc), config);
            url
        });
        urls.extend(external_api_docs);

        let swagger_resource = Resource::new(self.path.as_ref())
            .guard(Get())
            .app_data(Data::new(if let Some(config) = self.config {
                if config.url.is_some() || !config.urls.is_empty() {
                    config
                } else {
                    config.configure_defaults(urls)
                }
            } else {
                Config::new(urls)
            }))
            .to(serve_swagger_ui);

        HttpServiceFactory::register(swagger_resource, config);
    }
}

fn register_api_doc_url_resource(url: &str, api: ApiDoc, config: &mut actix_web::dev::AppService) {
    async fn get_api_doc(api_doc: web::Data<ApiDoc>) -> impl ActixResponder {
        HttpResponse::Ok().json(api_doc.as_ref())
    }

    let url_resource = Resource::new(url)
        .guard(Get())
        .app_data(Data::new(api))
        .to(get_api_doc);
    HttpServiceFactory::register(url_resource, config);
}

async fn serve_swagger_ui(path: web::Path<String>, data: web::Data<Config<'_>>) -> HttpResponse {
    match super::serve(&path.into_inner(), data.into_inner()) {
        Ok(swagger_file) => swagger_file
            .map(|file| {
                HttpResponse::Ok()
                    .content_type(file.content_type)
                    .body(file.bytes.to_vec())
            })
            .unwrap_or_else(|| HttpResponse::NotFound().finish()),
        Err(error) => HttpResponse::InternalServerError().body(error.to_string()),
    }
}