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
62mod config;
63#[cfg(feature = "redoc")]
64mod redoc;
65mod schemas;
66mod spec;
67#[cfg(feature = "swagger-ui")]
68mod swagger;
69
70// OpenAPI 3.1 support
71pub mod v31;
72
73// API versioning support
74pub mod versioning;
75
76pub use config::OpenApiConfig;
77pub use schemas::{
78    ErrorBodySchema, ErrorSchema, FieldErrorSchema, ValidationErrorBodySchema,
79    ValidationErrorSchema,
80};
81pub use spec::{
82    ApiInfo, MediaType, OpenApiSpec, Operation, OperationModifier, Parameter, PathItem,
83    RequestBody, ResponseModifier, ResponseSpec, SchemaRef,
84};
85
86// Re-export utoipa's ToSchema derive macro as Schema
87pub use utoipa::ToSchema as Schema;
88// Re-export utoipa's IntoParams derive macro
89pub use utoipa::IntoParams;
90
91// Re-export utoipa types for advanced usage
92pub mod utoipa_types {
93    pub use utoipa::{openapi, IntoParams, Modify, OpenApi, ToSchema};
94}
95
96use bytes::Bytes;
97use http::{header, Response, StatusCode};
98use http_body_util::Full;
99
100/// Generate OpenAPI JSON response
101pub fn openapi_json(spec: &OpenApiSpec) -> Response<Full<Bytes>> {
102    match serde_json::to_string_pretty(&spec.to_json()) {
103        Ok(json) => Response::builder()
104            .status(StatusCode::OK)
105            .header(header::CONTENT_TYPE, "application/json")
106            .body(Full::new(Bytes::from(json)))
107            .unwrap(),
108        Err(_) => Response::builder()
109            .status(StatusCode::INTERNAL_SERVER_ERROR)
110            .body(Full::new(Bytes::from("Failed to serialize OpenAPI spec")))
111            .unwrap(),
112    }
113}
114
115/// Generate Swagger UI HTML response
116#[cfg(feature = "swagger-ui")]
117pub fn swagger_ui_html(openapi_url: &str) -> Response<Full<Bytes>> {
118    let html = swagger::generate_swagger_html(openapi_url);
119    Response::builder()
120        .status(StatusCode::OK)
121        .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
122        .body(Full::new(Bytes::from(html)))
123        .unwrap()
124}
125
126/// Generate ReDoc HTML response
127///
128/// ReDoc provides a three-panel API documentation interface.
129///
130/// # Example
131/// ```rust,ignore
132/// use rustapi_openapi::redoc_html;
133/// let response = redoc_html("/openapi.json");
134/// ```
135#[cfg(feature = "redoc")]
136pub fn redoc_html(openapi_url: &str) -> Response<Full<Bytes>> {
137    let html = redoc::generate_redoc_html(openapi_url, None);
138    Response::builder()
139        .status(StatusCode::OK)
140        .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
141        .body(Full::new(Bytes::from(html)))
142        .unwrap()
143}
144
145/// Generate ReDoc HTML response with custom configuration
146#[cfg(feature = "redoc")]
147pub fn redoc_html_with_config(
148    openapi_url: &str,
149    title: Option<&str>,
150    config: &redoc::RedocConfig,
151) -> Response<Full<Bytes>> {
152    let html = redoc::generate_redoc_html_with_config(openapi_url, title, config);
153    Response::builder()
154        .status(StatusCode::OK)
155        .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
156        .body(Full::new(Bytes::from(html)))
157        .unwrap()
158}
159
160#[cfg(feature = "redoc")]
161pub use redoc::{RedocConfig, RedocTheme};
162
163/// Generate OpenAPI 3.1 JSON response
164pub fn openapi_31_json(spec: &v31::OpenApi31Spec) -> Response<Full<Bytes>> {
165    match serde_json::to_string_pretty(&spec) {
166        Ok(json) => Response::builder()
167            .status(StatusCode::OK)
168            .header(header::CONTENT_TYPE, "application/json")
169            .body(Full::new(Bytes::from(json)))
170            .unwrap(),
171        Err(_) => Response::builder()
172            .status(StatusCode::INTERNAL_SERVER_ERROR)
173            .body(Full::new(Bytes::from(
174                "Failed to serialize OpenAPI 3.1 spec",
175            )))
176            .unwrap(),
177    }
178}