swagger-ui2 0.6.0

Library to embed swagger-ui into your rust application. Entirely independent of web framework.
Documentation
mod config;

use bytes::Bytes;
use std::convert::TryInto;
use std::error::Error;
use std::fmt::Debug;
pub use config::{Config, Url};
use rust_embed::RustEmbed;
use http::response::Response;

#[derive(RustEmbed)]
#[folder = "swagger-ui-dist"]
struct SwaggerUiDist;

#[derive(Debug, Clone)]
pub struct SwaggerUi {
    config: Config,
    prefix: String,
}

const HTML_MIME: &str = "text/html; charset=utf-8";
const JS_MIME: &str = "application/javascript; charset=utf-8";
const CSS_MIME: &str = "text/css; charset=utf-8";
const PNG_MIME: &str = "image/png";
const DEFAULT_MIME: &str = "application/octet-stream";

impl SwaggerUi {
    pub fn url<U: Into<Url>>(mut self, u: U) -> Self {
        self.config.url(u);
        self
    }

    pub fn prefix(mut self, prefix: &str) -> Self {
        self.prefix = prefix.to_string();
        self
    }

    pub fn handle_url<U>(&self, url: U) -> Option<Response<Bytes>>
        where
            U: TryInto<http::Uri> + Debug,
            <U as TryInto<http::Uri>>::Error: Error
    {
        let url = url.try_into().unwrap();
        let path = url.path().strip_prefix(&self.prefix).unwrap();
        match path {
            "" | "/" => {
                let f = SwaggerUiDist::get("index.html").unwrap();
                let body = f.data.to_vec();
                Some(Response::builder()
                    .status(200)
                    .header("Content-Type", HTML_MIME)
                    .header("Content-Length", body.len())
                    .body(body.into())
                    .unwrap())
            }
            "/swagger-initializer.js" => {
                let f = SwaggerUiDist::get("swagger-initializer.js").unwrap();
                let body = String::from_utf8(f.data.to_vec()).unwrap();
                let config = serde_json::to_string(&self.config).unwrap();
                let body = body.replace("{config}", &config).into_bytes();
                Some(Response::builder()
                    .status(200)
                    .header("Content-Type", JS_MIME)
                    .header("Content-Length", body.len())
                    .body(body.into())
                    .unwrap())
            }
            z => {
                let f = SwaggerUiDist::get(&z[1..])?;
                let body = f.data.to_vec();
                let ext = std::path::Path::new(z).extension().unwrap().to_str().unwrap();
                let mime = match ext {
                    "html" => HTML_MIME,
                    "js" => JS_MIME,
                    "css" => CSS_MIME,
                    "png" => PNG_MIME,
                    _ => DEFAULT_MIME,
                };
                Some(Response::builder()
                    .status(200)
                    .header("Content-Type", mime)
                    .header("Content-Length", body.len())
                    .body(body.into())
                    .unwrap())
            }
        }
    }
}

impl Default for SwaggerUi {
    fn default() -> Self {
        Self {
            config: Config::default(),
            prefix: "".to_string(),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_swagger() {
        let ui = SwaggerUi::default();
        let res = ui.handle_url("/").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), HTML_MIME);

        let res = ui.handle_url("/swagger-initializer.js").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), JS_MIME);
        let body = String::from_utf8(res.body().to_vec()).unwrap();
        assert!(!body.contains("{config}"));

        let res = ui.handle_url("/swagger-ui.css").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), CSS_MIME);

        assert!(ui.handle_url("/not-found").is_none());
    }

    #[test]
    fn test_prefix_stripping() {
        let ui = SwaggerUi::default()
            .prefix("/docs");

        let res = ui.handle_url("/docs").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), HTML_MIME);

        let res = ui.handle_url("/docs/").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), HTML_MIME);

        let res = ui.handle_url("/docs/swagger-initializer.js").unwrap();
        assert_eq!(res.status(), 200);
        assert_eq!(res.headers().get("Content-Type").unwrap(), JS_MIME);

    }
}