# docs.rs - source
Source of the Rust file `src/docs.rs`.

    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
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124

    // Copyright (C) 2022  Aravinth Manivannan <realaravinth@batsense.net>
    // SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
    //
    // SPDX-License-Identifier: AGPL-3.0-or-later

    use std::borrow::Cow;

    use actix_web::body::BoxBody;
    use actix_web::{http::header, web, HttpResponse, Responder};
    use mime_guess::from_path;
    use rust_embed::RustEmbed;

    use crate::CACHE_AGE;

    pub const DOCS: routes::Docs = routes::Docs::new();

    pub mod routes {
        pub struct Docs {
            pub home: &'static str,
            pub spec: &'static str,
            pub assets: &'static str,
        }

        impl Docs {
            pub const fn new() -> Self {
                Docs {
                    home: "/docs/",
                    spec: "/docs/openapi.yaml",
                    assets: "/docs/{_:.*}",
                }
            }
        }
    }

    pub fn services(cfg: &mut web::ServiceConfig) {
        cfg.service(index).service(spec).service(dist);
    }

    #[derive(RustEmbed)]
    #[folder = "static/openapi/"]
    struct Asset;

    pub fn handle_embedded_file(path: &str) -> HttpResponse {
        match Asset::get(path) {
            Some(content) => {
                let body: BoxBody = match content.data {
                    Cow::Borrowed(bytes) => BoxBody::new(bytes),
                    Cow::Owned(bytes) => BoxBody::new(bytes),
                };

                HttpResponse::Ok()
                    .insert_header(header::CacheControl(vec![
                        header::CacheDirective::Public,
                        header::CacheDirective::Extension("immutable".into(), None),
                        header::CacheDirective::MaxAge(CACHE_AGE),
                    ]))
                    .content_type(from_path(path).first_or_octet_stream().as_ref())
                    .body(body)
            }
            None => HttpResponse::NotFound().body("404 Not Found"),
        }
    }

    #[my_codegen::get(path = "DOCS.assets")]
    async fn dist(path: web::Path<String>) -> impl Responder {
        handle_embedded_file(&path)
    }
    const OPEN_API_SPEC: &str = include_str!("../docs/openapi/dist/openapi.yaml");

    #[my_codegen::get(path = "DOCS.spec")]
    async fn spec() -> HttpResponse {
        HttpResponse::Ok()
            .content_type("text/yaml")
            .body(OPEN_API_SPEC)
    }

    #[my_codegen::get(path = "&DOCS.home[0..DOCS.home.len() -1]")]
    async fn index() -> HttpResponse {
        handle_embedded_file("index.html")
    }

    #[cfg(test)]
    mod tests {
        use actix_web::http::StatusCode;
        use actix_web::test;

        use super::*;
        use crate::*;

        #[actix_rt::test]
        async fn docs_works() {
            const FILE: &str = "favicon-32x32.png";

            let app = test::init_service(
                App::new()
                    .wrap(actix_middleware::NormalizePath::new(
                        actix_middleware::TrailingSlash::Trim,
                    ))
                    .configure(services),
            )
            .await;

            let resp = test::call_service(
                &app,
                test::TestRequest::get().uri(DOCS.home).to_request(),
            )
            .await;
            assert_eq!(resp.status(), StatusCode::OK);

            let resp = test::call_service(
                &app,
                test::TestRequest::get().uri(DOCS.spec).to_request(),
            )
            .await;
            assert_eq!(resp.status(), StatusCode::OK);

            let uri = format!("{}{}", DOCS.home, FILE);

            let resp =
                test::call_service(&app, test::TestRequest::get().uri(&uri).to_request())
                    .await;
            assert_eq!(resp.status(), StatusCode::OK);
        }
    }