tako_rs_core/openapi.rs
1//! `OpenAPI` documentation generation integrations.
2//!
3//! Tako ships two integration backends; pick exactly one per project:
4//!
5//! - **`utoipa` — primary, recommended**. Compile-time `OpenAPI` generation via
6//! derive macros, mature ecosystem, broad community use. Enable with the
7//! `utoipa` cargo feature.
8//! - **`vespera` — legacy / advanced**. Hand-rolled `OpenAPI` 3.1 builder plus
9//! route discovery. Kept opt-in for users who need the runtime spec model
10//! that `utoipa` does not provide. Enable with the `vespera` feature.
11//!
12//! Both backends share the same [`RouteOpenApi`](crate::openapi::RouteOpenApi) metadata attached on the
13//! `Router::route(...)` builder via `summary` / `description` / `tag` /
14//! `response`. The `OpenAPI` documents themselves are produced by whichever
15//! backend the application enables; do not enable both unless you are
16//! intentionally cross-validating the output.
17//!
18//! # Route-Level Integration
19//!
20//! Both integrations support route-level `OpenAPI` metadata:
21//!
22//! ```rust,ignore
23//! use tako::{router::Router, Method};
24//!
25//! let mut router = Router::new();
26//! router.route(Method::GET, "/users/{id}", get_user)
27//! .summary("Get user by ID")
28//! .description("Retrieves a user by their unique identifier")
29//! .tag("users")
30//! .response(200, "Successful response")
31//! .response(404, "User not found");
32//! ```
33//!
34//! # Examples
35//!
36//! ## Using utoipa
37//!
38//! ```rust,ignore
39//! use tako::openapi::utoipa::{OpenApi, OpenApiJson, ToSchema};
40//!
41//! #[derive(ToSchema)]
42//! struct User {
43//! id: u64,
44//! name: String,
45//! }
46//!
47//! #[derive(OpenApi)]
48//! #[openapi(components(schemas(User)))]
49//! struct ApiDoc;
50//!
51//! async fn openapi(_req: tako::types::Request) -> OpenApiJson {
52//! OpenApiJson(ApiDoc::openapi())
53//! }
54//! ```
55//!
56//! ## Using vespera
57//!
58//! ```rust,ignore
59//! use tako::openapi::vespera::{OpenApi, Info, VesperaOpenApiJson};
60//!
61//! async fn openapi(_req: tako::types::Request) -> VesperaOpenApiJson {
62//! let spec = OpenApi {
63//! info: Info {
64//! title: "My API".to_string(),
65//! version: "1.0.0".to_string(),
66//! ..Default::default()
67//! },
68//! ..Default::default()
69//! };
70//! VesperaOpenApiJson(spec)
71//! }
72//! ```
73
74use std::collections::BTreeMap;
75
76/// `OpenAPI` metadata that can be attached to a route.
77///
78/// This struct stores operation-level `OpenAPI` information that can be
79/// used to generate `OpenAPI` specifications from Tako routes.
80#[derive(Clone, Debug, Default)]
81pub struct RouteOpenApi {
82 /// Unique identifier for the operation.
83 pub operation_id: Option<String>,
84 /// Short summary of the operation.
85 pub summary: Option<String>,
86 /// Detailed description of the operation.
87 pub description: Option<String>,
88 /// Tags for grouping operations.
89 pub tags: Vec<String>,
90 /// Whether the operation is deprecated.
91 pub deprecated: bool,
92 /// Response descriptions keyed by status code.
93 pub responses: BTreeMap<u16, String>,
94 /// Parameter descriptions.
95 pub parameters: Vec<OpenApiParameter>,
96 /// Request body description.
97 pub request_body: Option<OpenApiRequestBody>,
98 /// Security requirements.
99 pub security: Vec<String>,
100}
101
102/// `OpenAPI` parameter definition.
103#[derive(Clone, Debug, Default)]
104pub struct OpenApiParameter {
105 /// Parameter name.
106 pub name: String,
107 /// Parameter location (query, header, path, cookie).
108 pub location: ParameterLocation,
109 /// Parameter description.
110 pub description: Option<String>,
111 /// Whether the parameter is required.
112 pub required: bool,
113}
114
115/// Location of an `OpenAPI` parameter.
116#[derive(Clone, Debug, Default)]
117pub enum ParameterLocation {
118 #[default]
119 Query,
120 Header,
121 Path,
122 Cookie,
123}
124
125/// `OpenAPI` request body definition.
126#[derive(Clone, Debug, Default)]
127pub struct OpenApiRequestBody {
128 /// Description of the request body.
129 pub description: Option<String>,
130 /// Whether the request body is required.
131 pub required: bool,
132 /// Content type (e.g., "application/json").
133 pub content_type: String,
134 /// Schema properties for the request body (name, type, description).
135 pub schema_properties: Vec<RequestBodyProperty>,
136}
137
138/// A property definition for request body schema.
139#[derive(Clone, Debug, Default)]
140pub struct RequestBodyProperty {
141 /// Property name.
142 pub name: String,
143 /// Property type (e.g., "string", "integer", "boolean").
144 pub property_type: String,
145 /// Property description.
146 pub description: Option<String>,
147}
148
149#[cfg(feature = "utoipa")]
150#[cfg_attr(docsrs, doc(cfg(feature = "utoipa")))]
151pub mod utoipa;
152
153#[cfg(feature = "vespera")]
154#[cfg_attr(docsrs, doc(cfg(feature = "vespera")))]
155pub mod vespera;
156
157/// `OpenAPI` UI helpers (Swagger UI, Scalar, `RapiDoc`, `Redoc`).
158pub mod ui;