Skip to main content

tako_rs_core/route/
openapi.rs

1//! `OpenAPI` metadata attachment for a route.
2//!
3//! The chainable builder methods that record `OpenAPI` documentation
4//! (operation id, summary, description, tags, deprecation, responses,
5//! parameters, request body, security) onto the route's `RouteOpenApi`
6//! store, plus the accessor that reads it back. Compiled only when an
7//! `OpenAPI` backend feature is enabled.
8
9#![cfg(any(feature = "utoipa", feature = "vespera"))]
10
11use super::Route;
12use crate::openapi::RouteOpenApi;
13
14impl Route {
15  /// Sets a unique operation ID for this route in `OpenAPI` documentation.
16  ///
17  /// # Examples
18  ///
19  /// ```rust,ignore
20  /// router.route(Method::GET, "/users", list_users)
21  ///     .operation_id("listUsers");
22  /// ```
23  #[cfg(any(feature = "utoipa", feature = "vespera"))]
24  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
25  pub fn operation_id(&self, id: impl Into<String>) -> &Self {
26    let mut guard = self.openapi.write();
27    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
28    openapi.operation_id = Some(id.into());
29    self
30  }
31
32  /// Sets a short summary for this route in `OpenAPI` documentation.
33  ///
34  /// # Examples
35  ///
36  /// ```rust,ignore
37  /// router.route(Method::GET, "/users/{id}", get_user)
38  ///     .summary("Get user by ID");
39  /// ```
40  #[cfg(any(feature = "utoipa", feature = "vespera"))]
41  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
42  pub fn summary(&self, summary: impl Into<String>) -> &Self {
43    let mut guard = self.openapi.write();
44    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
45    openapi.summary = Some(summary.into());
46    self
47  }
48
49  /// Sets a detailed description for this route in `OpenAPI` documentation.
50  ///
51  /// # Examples
52  ///
53  /// ```rust,ignore
54  /// router.route(Method::GET, "/users/{id}", get_user)
55  ///     .description("Retrieves a user by their unique identifier");
56  /// ```
57  #[cfg(any(feature = "utoipa", feature = "vespera"))]
58  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
59  pub fn description(&self, description: impl Into<String>) -> &Self {
60    let mut guard = self.openapi.write();
61    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
62    openapi.description = Some(description.into());
63    self
64  }
65
66  /// Adds a tag to group this route in `OpenAPI` documentation.
67  ///
68  /// # Examples
69  ///
70  /// ```rust,ignore
71  /// router.route(Method::GET, "/users", list_users)
72  ///     .tag("users")
73  ///     .tag("public");
74  /// ```
75  #[cfg(any(feature = "utoipa", feature = "vespera"))]
76  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
77  pub fn tag(&self, tag: impl Into<String>) -> &Self {
78    let mut guard = self.openapi.write();
79    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
80    openapi.tags.push(tag.into());
81    self
82  }
83
84  /// Marks this route as deprecated in `OpenAPI` documentation.
85  ///
86  /// # Examples
87  ///
88  /// ```rust,ignore
89  /// router.route(Method::GET, "/v1/users", list_users_v1)
90  ///     .deprecated();
91  /// ```
92  #[cfg(any(feature = "utoipa", feature = "vespera"))]
93  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
94  pub fn deprecated(&self) -> &Self {
95    let mut guard = self.openapi.write();
96    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
97    openapi.deprecated = true;
98    self
99  }
100
101  /// Adds a response description for a status code in `OpenAPI` documentation.
102  ///
103  /// # Examples
104  ///
105  /// ```rust,ignore
106  /// router.route(Method::GET, "/users/{id}", get_user)
107  ///     .response(200, "Successful response with user data")
108  ///     .response(404, "User not found");
109  /// ```
110  #[cfg(any(feature = "utoipa", feature = "vespera"))]
111  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
112  pub fn response(&self, status: u16, description: impl Into<String>) -> &Self {
113    let mut guard = self.openapi.write();
114    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
115    openapi.responses.insert(status, description.into());
116    self
117  }
118
119  /// Adds a parameter definition for this route in `OpenAPI` documentation.
120  ///
121  /// # Examples
122  ///
123  /// ```rust,ignore
124  /// use tako::openapi::{OpenApiParameter, ParameterLocation};
125  ///
126  /// router.route(Method::GET, "/users", list_users)
127  ///     .parameter(OpenApiParameter {
128  ///         name: "limit".to_string(),
129  ///         location: ParameterLocation::Query,
130  ///         description: Some("Maximum number of results".to_string()),
131  ///         required: false,
132  ///     });
133  /// ```
134  #[cfg(any(feature = "utoipa", feature = "vespera"))]
135  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
136  pub fn parameter(&self, param: crate::openapi::OpenApiParameter) -> &Self {
137    let mut guard = self.openapi.write();
138    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
139    openapi.parameters.push(param);
140    self
141  }
142
143  /// Sets the request body description for this route in `OpenAPI` documentation.
144  ///
145  /// # Examples
146  ///
147  /// ```rust,ignore
148  /// use tako::openapi::OpenApiRequestBody;
149  ///
150  /// router.route(Method::POST, "/users", create_user)
151  ///     .request_body(OpenApiRequestBody {
152  ///         description: Some("User data to create".to_string()),
153  ///         required: true,
154  ///         content_type: "application/json".to_string(),
155  ///     });
156  /// ```
157  #[cfg(any(feature = "utoipa", feature = "vespera"))]
158  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
159  pub fn request_body(&self, body: crate::openapi::OpenApiRequestBody) -> &Self {
160    let mut guard = self.openapi.write();
161    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
162    openapi.request_body = Some(body);
163    self
164  }
165
166  /// Adds a security requirement for this route in `OpenAPI` documentation.
167  ///
168  /// # Examples
169  ///
170  /// ```rust,ignore
171  /// router.route(Method::DELETE, "/users/{id}", delete_user)
172  ///     .security("bearerAuth");
173  /// ```
174  #[cfg(any(feature = "utoipa", feature = "vespera"))]
175  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
176  pub fn security(&self, requirement: impl Into<String>) -> &Self {
177    let mut guard = self.openapi.write();
178    let openapi = guard.get_or_insert_with(RouteOpenApi::default);
179    openapi.security.push(requirement.into());
180    self
181  }
182
183  /// Returns a clone of the `OpenAPI` metadata for this route, if any.
184  #[cfg(any(feature = "utoipa", feature = "vespera"))]
185  #[cfg_attr(docsrs, doc(cfg(any(feature = "utoipa", feature = "vespera"))))]
186  pub fn openapi_metadata(&self) -> Option<RouteOpenApi> {
187    self.openapi.read().clone()
188  }
189}