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;
63mod schemas;
64mod spec;
65#[cfg(feature = "swagger-ui")]
66mod swagger;
67
68// OpenAPI 3.1 support
69pub mod v31;
70
71// API versioning support
72pub mod versioning;
73
74pub use config::OpenApiConfig;
75pub use schemas::{
76 ErrorBodySchema, ErrorSchema, FieldErrorSchema, ValidationErrorBodySchema,
77 ValidationErrorSchema,
78};
79pub use spec::{
80 ApiInfo, MediaType, OpenApiSpec, Operation, OperationModifier, Parameter, PathItem,
81 RequestBody, ResponseModifier, ResponseSpec, SchemaRef,
82};
83
84// Re-export utoipa's ToSchema derive macro as Schema
85pub use utoipa::ToSchema as Schema;
86// Re-export utoipa's IntoParams derive macro
87pub use utoipa::IntoParams;
88
89// Re-export utoipa types for advanced usage
90pub mod utoipa_types {
91 pub use utoipa::{openapi, IntoParams, Modify, OpenApi, ToSchema};
92}
93
94use bytes::Bytes;
95use http::{header, Response, StatusCode};
96use http_body_util::Full;
97
98/// Generate OpenAPI JSON response
99pub fn openapi_json(spec: &OpenApiSpec) -> Response<Full<Bytes>> {
100 match serde_json::to_string_pretty(&spec.to_json()) {
101 Ok(json) => Response::builder()
102 .status(StatusCode::OK)
103 .header(header::CONTENT_TYPE, "application/json")
104 .body(Full::new(Bytes::from(json)))
105 .unwrap(),
106 Err(_) => Response::builder()
107 .status(StatusCode::INTERNAL_SERVER_ERROR)
108 .body(Full::new(Bytes::from("Failed to serialize OpenAPI spec")))
109 .unwrap(),
110 }
111}
112
113/// Generate Swagger UI HTML response
114#[cfg(feature = "swagger-ui")]
115pub fn swagger_ui_html(openapi_url: &str) -> Response<Full<Bytes>> {
116 let html = swagger::generate_swagger_html(openapi_url);
117 Response::builder()
118 .status(StatusCode::OK)
119 .header(header::CONTENT_TYPE, "text/html; charset=utf-8")
120 .body(Full::new(Bytes::from(html)))
121 .unwrap()
122}
123
124/// Generate OpenAPI 3.1 JSON response
125pub fn openapi_31_json(spec: &v31::OpenApi31Spec) -> Response<Full<Bytes>> {
126 match serde_json::to_string_pretty(&spec) {
127 Ok(json) => Response::builder()
128 .status(StatusCode::OK)
129 .header(header::CONTENT_TYPE, "application/json")
130 .body(Full::new(Bytes::from(json)))
131 .unwrap(),
132 Err(_) => Response::builder()
133 .status(StatusCode::INTERNAL_SERVER_ERROR)
134 .body(Full::new(Bytes::from(
135 "Failed to serialize OpenAPI 3.1 spec",
136 )))
137 .unwrap(),
138 }
139}