rustapi_openapi/lib.rs
1//! OpenAPI documentation for RustAPI
2//!
3//! This crate provides OpenAPI specification generation and Swagger UI serving
4//! for RustAPI applications. It wraps `utoipa` internally while providing a
5//! clean public API.
6//!
7//! # Features
8//!
9//! - **OpenAPI 3.0.3** and **OpenAPI 3.1.0** specification support
10//! - Swagger UI serving at `/docs`
11//! - JSON spec at `/openapi.json`
12//! - Schema derivation via `#[derive(Schema)]`
13//! - **API versioning** with multiple strategies (path, header, query, accept)
14//! - **JSON Schema 2020-12** support for OpenAPI 3.1
15//! - **Webhook definitions** support
16//!
17//! # OpenAPI 3.1 Usage
18//!
19//! ```rust,ignore
20//! use rustapi_openapi::v31::{OpenApi31Spec, Webhook, JsonSchema2020};
21//!
22//! let spec = OpenApi31Spec::new("My API", "1.0.0")
23//! .description("API with OpenAPI 3.1 support")
24//! .webhook("orderPlaced", Webhook::with_summary("Order notification"))
25//! .schema("User", JsonSchema2020::object()
26//! .with_property("id", JsonSchema2020::integer())
27//! .with_property("name", JsonSchema2020::string())
28//! .with_required("id"))
29//! .build();
30//! ```
31//!
32//! # API Versioning Usage
33//!
34//! ```rust,ignore
35//! use rustapi_openapi::versioning::{VersionRouter, ApiVersion, VersionStrategy};
36//!
37//! let router = VersionRouter::new()
38//! .strategy(VersionStrategy::path())
39//! .default_version(ApiVersion::v1())
40//! .version(ApiVersion::v1(), VersionedRouteConfig::version(ApiVersion::v1()))
41//! .version(ApiVersion::v2(), VersionedRouteConfig::version(ApiVersion::v2()));
42//! ```
43//!
44//! # Legacy Usage (OpenAPI 3.0)
45//!
46//! ```rust,ignore
47//! use rustapi_rs::prelude::*;
48//!
49//! #[derive(Serialize, Schema)]
50//! struct User {
51//! id: i64,
52//! name: String,
53//! }
54//!
55//! RustApi::new()
56//! .route("/users", get(list_users))
57//! .docs("/docs")
58//! .run("127.0.0.1:8080")
59//! .await
60//! ```
61
62// Needed for proc-macro to refer to this crate
63extern crate self as rustapi_openapi;
64
65mod config;
66#[cfg(feature = "redoc")]
67mod redoc;
68pub mod schema;
69mod schemas;
70mod spec;
71#[cfg(feature = "swagger-ui")]
72mod swagger;
73#[cfg(test)]
74mod tests;
75
76// API versioning support
77pub mod versioning;
78
79pub use config::OpenApiConfig;
80pub use schemas::{
81 ErrorBodySchema, ErrorSchema, FieldErrorSchema, ValidationErrorBodySchema,
82 ValidationErrorSchema,
83};
84pub use spec::{
85 ApiInfo, MediaType, OpenApiSpec, Operation, OperationModifier, Parameter, PathItem,
86 RequestBody, ResponseModifier, ResponseSpec, SchemaRef,
87};
88
89// Re-export Schema derive macro
90pub use rustapi_macros::Schema;
91
92use bytes::Bytes;
93use http::{header, Response, StatusCode};
94use http_body_util::Full;
95
96/// Generate OpenAPI JSON response
97pub fn openapi_json(spec: &OpenApiSpec) -> Response<Full<Bytes>> {
98 match serde_json::to_string_pretty(&spec.to_json()) {
99 Ok(json) => Response::builder()
100 .status(StatusCode::OK)
101 .header(header::CONTENT_TYPE, "application/json")
102 .body(Full::new(Bytes::from(json)))
103 .unwrap(),
104 Err(_) => Response::builder()
105 .status(StatusCode::INTERNAL_SERVER_ERROR)
106 .body(Full::new(Bytes::from("Failed to serialize OpenAPI spec")))
107 .unwrap(),
108 }
109}
110
111/// Generate Swagger UI HTML response
112#[cfg(feature = "swagger-ui")]
113pub fn swagger_ui_html(openapi_url: &str) -> Response<Full<Bytes>> {
114 let html = swagger::generate_swagger_html(openapi_url);
115 Response::builder()
116 .status(StatusCode::OK)
117 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
118 .body(Full::new(Bytes::from(html)))
119 .unwrap()
120}
121
122/// Generate ReDoc HTML response
123///
124/// ReDoc provides a three-panel API documentation interface.
125///
126/// # Example
127/// ```rust,ignore
128/// use rustapi_openapi::redoc_html;
129/// let response = redoc_html("/openapi.json");
130/// ```
131#[cfg(feature = "redoc")]
132pub fn redoc_html(openapi_url: &str) -> Response<Full<Bytes>> {
133 let html = redoc::generate_redoc_html(openapi_url, None);
134 Response::builder()
135 .status(StatusCode::OK)
136 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
137 .body(Full::new(Bytes::from(html)))
138 .unwrap()
139}
140
141/// Generate ReDoc HTML response with custom configuration
142#[cfg(feature = "redoc")]
143pub fn redoc_html_with_config(
144 openapi_url: &str,
145 title: Option<&str>,
146 config: &redoc::RedocConfig,
147) -> Response<Full<Bytes>> {
148 let html = redoc::generate_redoc_html_with_config(openapi_url, title, config);
149 Response::builder()
150 .status(StatusCode::OK)
151 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
152 .body(Full::new(Bytes::from(html)))
153 .unwrap()
154}
155
156#[cfg(feature = "redoc")]
157pub use redoc::{RedocConfig, RedocTheme};