Skip to main content

sz_rust_http_facade/
openapi.rs

1//! OpenAPI 3.0 文档自动生成
2//!
3//! 基于 utoipa 提供 OpenAPI spec 构建和 Swagger UI 集成。
4
5use thiserror::Error;
6
7/// OpenAPI 错误
8#[derive(Debug, Error)]
9pub enum OpenApiError {
10    /// 构建错误
11    #[error("OpenAPI build error: {0}")]
12    Build(String),
13    /// JSON 序列化错误
14    #[error("JSON serialize error: {0}")]
15    Serialize(String),
16}
17
18/// OpenAPI 构建器
19///
20/// 封装 utoipa::OpenApi 构建过程,提供链式 API。
21pub struct OpenApiBuilder {
22    title: String,
23    version: String,
24    description: Option<String>,
25}
26
27impl OpenApiBuilder {
28    /// 创建 OpenAPI 构建器
29    pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
30        Self {
31            title: title.into(),
32            version: version.into(),
33            description: None,
34        }
35    }
36
37    /// 设置描述
38    pub fn description(mut self, desc: impl Into<String>) -> Self {
39        self.description = Some(desc.into());
40        self
41    }
42
43    /// 构建 OpenApi spec
44    pub fn build(self) -> utoipa::openapi::Info {
45        let mut info = utoipa::openapi::Info::default();
46        info.title = self.title;
47        info.version = self.version;
48        info.description = self.description;
49        info
50    }
51
52    /// 构建并序列化为 JSON
53    pub fn to_json(self) -> Result<String, OpenApiError> {
54        let info = self.build();
55        serde_json::to_string_pretty(&info).map_err(|e| OpenApiError::Serialize(e.to_string()))
56    }
57
58    /// 验证 OpenAPI spec
59    pub fn validate(self) -> Result<(), OpenApiError> {
60        let info = self.build();
61        if info.title.is_empty() {
62            return Err(OpenApiError::Build("title is empty".into()));
63        }
64        if info.version.is_empty() {
65            return Err(OpenApiError::Build("version is empty".into()));
66        }
67        Ok(())
68    }
69}
70
71/// 挂载 Swagger UI 路由
72///
73/// 需启用 `swagger-ui` feature。
74/// utoipa-swagger-ui 8.x 依赖 axum 0.7,与本 crate axum 0.8 不兼容,
75/// 返回占位路由;实际 Swagger UI 由应用层直接挂载。
76#[cfg(feature = "swagger-ui")]
77pub fn swagger_ui_routes() -> axum::Router {
78    axum::Router::new().route(
79        "/docs/{_:.*}",
80        axum::routing::get(|| async { "Swagger UI" }),
81    )
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_openapi_build() {
90        let info = OpenApiBuilder::new("Test API", "1.0.0").build();
91        assert_eq!(info.title, "Test API");
92        assert_eq!(info.version, "1.0.0");
93    }
94
95    #[test]
96    fn test_openapi_to_json() {
97        let json = OpenApiBuilder::new("Test API", "1.0.0").to_json().unwrap();
98        assert!(json.contains("Test API"));
99        assert!(json.contains("1.0.0"));
100    }
101
102    #[test]
103    fn test_openapi_validate() {
104        let result = OpenApiBuilder::new("Test", "1.0").validate();
105        assert!(result.is_ok());
106    }
107
108    #[test]
109    fn test_openapi_validate_empty_title() {
110        let result = OpenApiBuilder::new("", "1.0").validate();
111        assert!(result.is_err());
112    }
113
114    #[test]
115    fn test_openapi_error_display() {
116        let err = OpenApiError::Build("test error".into());
117        assert_eq!(err.to_string(), "OpenAPI build error: test error");
118    }
119}