use crate::spec::OpenApiSpec;
use armature_core::{Error, HttpResponse};
#[derive(Debug, Clone)]
pub struct SwaggerConfig {
pub path: String,
pub title: String,
pub spec: OpenApiSpec,
}
impl SwaggerConfig {
pub fn new(path: impl Into<String>, spec: OpenApiSpec) -> Self {
Self {
path: path.into(),
title: "API Documentation".to_string(),
spec,
}
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn spec_json(&self) -> Result<String, Error> {
serde_json::to_string_pretty(&self.spec)
.map_err(|e| Error::Internal(format!("Failed to serialize spec: {}", e)))
}
pub fn spec_yaml(&self) -> Result<String, Error> {
serde_yaml::to_string(&self.spec)
.map_err(|e| Error::Internal(format!("Failed to serialize spec: {}", e)))
}
}
pub fn swagger_ui_response(config: &SwaggerConfig) -> Result<HttpResponse, Error> {
let spec_json = config.spec_json()?;
let spec_json_safe = spec_json.replace("</", "<\\/");
let html = format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.10.0/swagger-ui.css">
<style>
body {{
margin: 0;
padding: 0;
}}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.10.0/swagger-ui-bundle.js"></script>
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.10.0/swagger-ui-standalone-preset.js"></script>
<script>
window.onload = function() {{
const spec = {spec};
SwaggerUIBundle({{
spec: spec,
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
}});
}};
</script>
</body>
</html>"#,
title = config.title,
spec = spec_json_safe
);
Ok(HttpResponse::ok()
.with_header("content-type".to_string(), "text/html".to_string())
.with_body(html.into_bytes()))
}
pub fn spec_json_response(config: &SwaggerConfig) -> Result<HttpResponse, Error> {
let spec_json = config.spec_json()?;
Ok(HttpResponse::ok()
.with_header("content-type".to_string(), "application/json".to_string())
.with_body(spec_json.into_bytes()))
}
pub fn spec_yaml_response(config: &SwaggerConfig) -> Result<HttpResponse, Error> {
let spec_yaml = config.spec_yaml()?;
Ok(HttpResponse::ok()
.with_header("content-type".to_string(), "application/x-yaml".to_string())
.with_body(spec_yaml.into_bytes()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::builder::OpenApiBuilder;
#[test]
fn test_swagger_config() {
let spec = OpenApiBuilder::new("Test API", "1.0.0").build();
let config = SwaggerConfig::new("/api-docs", spec).with_title("Test Docs");
assert_eq!(config.path, "/api-docs");
assert_eq!(config.title, "Test Docs");
}
#[test]
fn test_spec_json() {
let spec = OpenApiBuilder::new("Test API", "1.0.0")
.description("A test API")
.build();
let config = SwaggerConfig::new("/api-docs", spec);
let json = config.spec_json().unwrap();
assert!(json.contains("Test API"));
assert!(json.contains("1.0.0"));
}
#[test]
fn test_spec_yaml() {
let spec = OpenApiBuilder::new("Test API", "1.0.0")
.description("A test API")
.build();
let config = SwaggerConfig::new("/api-docs", spec);
let yaml = config.spec_yaml().unwrap();
assert!(yaml.contains("Test API"));
assert!(yaml.contains("1.0.0"));
}
}