rocket_autodocu/lib.rs
1#![forbid(missing_docs)]
2#![forbid(unsafe_code)]
3#![deny(clippy::all)]
4
5//! This projects serves to enable automatic rendering of `openapi.json` files, and provides
6//! facilities to also serve the documentation alongside your api.
7//!
8//! # Usage
9//! First, add the following lines to your `Cargo.toml`
10//! ```toml
11//! [dependencies]
12//! rocket = { version = "0.5.0-rc.1", default-features = false, features = ["json"] }
13//! schemars = "0.8"
14//! autodocu = { version = "0.6.0-alpha-1" }
15//! rocket_autodocu = { version = "0.9.1", features = ["swagger"] }
16//! ```
17//! To add documentation to a set of endpoints, a couple of steps are required. The request and
18//! response types of the endpoint must implement `JsonSchema`. Secondly, the function must be
19//! marked with `#[openapi]`. After that, you can simply replace `routes!` with
20//! `openapi_get_routes!`. This will append an additional route to the resulting `Vec<Route>`,
21//! which contains the `openapi.json` file that is required by swagger. Now that we have the json
22//! file that we need, we can serve the swagger web interface at another endpoint, and we should be
23//! able to load the example in the browser!
24//! ### Example
25//! ```rust, no_run
26//! use rocket::get;
27//! use rocket::serde::json::Json;
28//! use rocket_autodocu::{openapi, openapi_get_routes, JsonSchema};
29//! use rocket_autodocu::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
30//!
31//! #[derive(serde::Serialize, JsonSchema)]
32//! struct Response {
33//! reply: String,
34//! }
35//!
36//! #[openapi]
37//! #[get("/")]
38//! fn my_controller() -> Json<Response> {
39//! Json(Response {
40//! reply: "show me the docs!".to_string(),
41//! })
42//! }
43//!
44//! fn get_docs() -> SwaggerUIConfig {
45//! use rocket_autodocu::settings::UrlObject;
46//!
47//! SwaggerUIConfig {
48//! url: "/my_resource/openapi.json".to_string(),
49//! ..Default::default()
50//! }
51//! }
52//!
53//! fn main() {
54//! rocket::build()
55//! .mount("/my_resource", openapi_get_routes![my_controller])
56//! .mount("/swagger", make_swagger_ui(&get_docs()))
57//! .launch();
58//! }
59//! ```
60//!
61//! This crate exposes a few macros that can be used to generate and serve routes and OpenApi objects.
62//! - `mount_endpoints_and_merged_docs!{...}`: Mount endpoints and mount merged OpenAPI documentation.
63//! - `openapi_get_routes![...]`: To generate and add the `openapi.json` route.
64//! - `openapi_get_routes_spec![...]`: To generate and return a list of routes and the openapi spec.
65//! - `openapi_get_spec![...]`: To generate and return the openapi spec.
66//!
67//! The last 3 macros have very similar behavior, but differ in what they return.
68//! Here is a list of the marcos and what they return:
69//! - `openapi_get_routes![...]`: `Vec<rocket::Route>` (adds route for `openapi.json`)
70//! - `openapi_get_routes_spec![...]`: `(Vec<rocket::Route>, autodocu::openapi3::OpenApi)`
71//! - `openapi_get_spec![...]`: `autodocu::openapi3::OpenApi`
72//!
73
74mod error;
75
76/// Contains the `Generator` struct, which you can use to manually control the way a struct is
77/// represented in the documentation.
78pub mod gen;
79/// Contains several `Rocket` `Handler`s, which are used for serving the json files and the swagger
80/// interface.
81pub mod handlers;
82/// Contains the functions and structs required to display the RapiDoc UI.
83#[cfg(feature = "rapidoc")]
84pub mod rapidoc;
85/// This module contains several traits that correspond to the `Rocket` traits pertaining to request
86/// guards and responses
87pub mod request;
88/// Contains the trait `OpenApiResponder`, meaning that a response implementing this trait can be
89/// documented.
90pub mod response;
91/// Contains then `OpenApiSettings` struct, which can be used to customize the behavior of a
92/// `Generator`.
93pub mod settings;
94/// Contains the functions and structs required to display the Swagger UI.
95#[cfg(feature = "swagger")]
96pub mod swagger_ui;
97/// Assorted function that are used throughout the application.
98pub mod util;
99
100pub use error::*;
101/// Re-export autodocu
102pub use autodocu;
103pub use rocket_autodocu_codegen::*;
104pub use schemars::JsonSchema;
105
106/// Contains information about an endpoint.
107pub struct OperationInfo {
108 /// The path of the endpoint
109 pub path: String,
110 /// The HTTP Method of this endpoint.
111 pub method: rocket::http::Method,
112 /// Contains information to be showed in the documentation about this endpoint.
113 pub operation: autodocu::openapi3::Operation,
114}
115
116/// Convert OpenApi object to routable endpoint.
117///
118/// Used to serve an `OpenApi` object as an `openapi.json` file in Rocket.
119pub fn get_openapi_route(
120 spec: autodocu::openapi3::OpenApi,
121 settings: &settings::OpenApiSettings,
122) -> rocket::Route {
123 handlers::OpenApiHandler::new(spec).into_route(&settings.json_path)
124}
125
126/// Mount endpoints and mount merged OpenAPI documentation.
127///
128/// This marco just makes to code look cleaner and improves readability
129/// for bigger codebases.
130///
131/// The macro expects the following arguments:
132/// - rocket_builder: `Rocket<Build>`,
133/// - base_path: `&str`, `String` or [`Uri`](rocket::http::uri::Uri). (Anything that implements `ToString`)
134/// Anything accepted by [`mount()`](https://docs.rs/rocket/0.5.0-rc.1/rocket/struct.Rocket.html#method.mount)
135/// - openapi_settings: `OpenApiSettings` (use `OpenApiSettings::default()` if default settings are okay for you),
136/// - List of (0 or more):
137/// - path: `&str`, `String` or [`Uri`](rocket::http::uri::Uri).
138/// Anything accepted by `mount()` (`base_path` should not be included).
139/// - `=>`: divider
140/// - route_and_docs: `(Vec<rocket::Route>, OpenApi)`
141///
142/// Example:
143/// ```rust,ignore
144/// let custom_route_spec = (vec![], custom_spec());
145/// mount_endpoints_and_merged_docs! {
146/// building_rocket, "/v1".to_owned(),
147/// "/" => custom_route_spec,
148/// "/post" => post::get_routes_and_docs(),
149/// "/message" => message::get_routes_and_docs(),
150/// };
151/// ```
152///
153#[macro_export]
154macro_rules! mount_endpoints_and_merged_docs {
155 ($rocket_builder:ident, $base_path:expr, $openapi_settings:ident,
156 $($path:expr => $route_and_docs:expr),* $(,)*) => {{
157 let base_path = $base_path.to_string();
158 assert!(base_path == "/" || !base_path.ends_with("/"), "`base_path` should not end with an `/`.");
159 let mut openapi_list: Vec<(_, rocket_autodocu::autodocu::openapi3::OpenApi)> = Vec::new();
160 $({
161 let (routes, openapi) = $route_and_docs;
162 $rocket_builder = $rocket_builder.mount(format!("{}{}", base_path, $path), routes);
163 openapi_list.push(($path, openapi));
164 })*
165 // Combine all OpenApi documentation into one struct.
166 let openapi_docs = match rocket_autodocu::autodocu::merge::marge_spec_list(&openapi_list){
167 Ok(docs) => docs,
168 Err(err) => panic!("Could not merge OpenAPI spec: {}", err),
169 };
170 // Add OpenApi route
171 $rocket_builder = $rocket_builder.mount(
172 $base_path,
173 vec![rocket_autodocu::get_openapi_route(
174 openapi_docs,
175 &$openapi_settings,
176 )],
177 );
178 }};
179}
180
181/// A replacement macro for `rocket::routes`. This also takes a optional settings object.
182///
183/// The key differences are that this macro will add an additional element to the
184/// resulting `Vec<rocket::Route>`, which serves a static file called
185/// `openapi.json`. This file can then be used to display the routes in the Swagger/RapiDoc UI.
186///
187/// Example:
188/// ```rust,ignore
189/// use autodocu::openapi3::OpenApi;
190/// let settings = rocket_autodocu::settings::OpenApiSettings::new();
191/// let routes: Vec<rocket::Route> =
192/// openapi_get_routes![settings: create_message, get_message];
193/// ```
194/// Or
195/// ```rust,ignore
196/// use autodocu::openapi3::OpenApi;
197/// let routes: Vec<rocket::Route> =
198/// openapi_get_routes![create_message, get_message];
199/// ```
200#[macro_export]
201macro_rules! openapi_get_routes {
202 // With settings
203 ($settings:ident :
204 $($route:expr),* $(,)*) => {{
205 let spec = rocket_autodocu::openapi_spec;
206 let routes = rocket_autodocu::openapi_routes, &$settings);
207 routes
208 }};
209
210 // Without settings
211 ($($route:expr),* $(,)*) => {{
212 let settings = rocket_autodocu::settings::OpenApiSettings::new();
213 rocket_autodocu::openapi_get_routes![settings: $($route),*]
214 }};
215}
216
217/// A replacement macro for `rocket::routes`. This parses the routes and provides
218/// a tuple with 2 parts `(Vec<rocket::Route>, OpenApi)`:
219/// - `Vec<rocket::Route>`: A list of all the routes that `rocket::routes![]` would have provided.
220/// - `OpenApi`: The `autodocu::openapi3::OpenApi` spec for all the routes.
221///
222/// NOTE: This marco is different from `openapi_get_routes` in that this does not add
223/// the `openapi.json` file to the list of routes. This is done so the `OpenApi` spec can be changed
224/// before serving it.
225///
226/// Example:
227/// ```rust,ignore
228/// use autodocu::openapi3::OpenApi;
229/// let settings = rocket_autodocu::settings::OpenApiSettings::new();
230/// let (routes, spec): (Vec<rocket::Route>, OpenApi) =
231/// openapi_get_routes_spec![settings: create_message, get_message];
232/// ```
233/// Or
234/// ```rust,ignore
235/// use autodocu::openapi3::OpenApi;
236/// let (routes, spec): (Vec<rocket::Route>, OpenApi) =
237/// openapi_get_routes_spec![create_message, get_message];
238/// ```
239#[macro_export]
240macro_rules! openapi_get_routes_spec {
241 // With settings
242 ($settings:ident :
243 $($route:expr),* $(,)*) => {{
244 let spec = rocket_autodocu::openapi_spec;
245 let routes = rocket_autodocu::openapi_routes;
246 (routes, spec)
247 }};
248
249 // Without settings
250 ($($route:expr),* $(,)*) => {{
251 let settings = rocket_autodocu::settings::OpenApiSettings::new();
252 rocket_autodocu::openapi_get_routes_spec![settings: $($route),*]
253 }};
254}
255
256/// Generate `OpenApi` spec only, does not generate routes.
257/// This can be used in cases where you are only interested in the openAPI spec, but not in the routes.
258/// A use case could be inside of `build.rs` scripts or where you want to alter OpenAPI object
259/// at runtime.
260///
261/// Example:
262/// ```rust,ignore
263/// use autodocu::openapi3::OpenApi;
264/// let settings = rocket_autodocu::settings::OpenApiSettings::new();
265/// let spec: OpenApi = openapi_get_spec![settings: create_message, get_message];
266/// ```
267/// Or
268/// ```rust,ignore
269/// use autodocu::openapi3::OpenApi;
270/// let spec: OpenApi = openapi_get_spec![create_message, get_message];
271/// ```
272#[macro_export]
273macro_rules! openapi_get_spec {
274 // With settings
275 ($settings:ident :
276 $($route:expr),* $(,)*) => {{
277 let spec = rocket_autodocu::openapi_spec;
278 spec
279 }};
280
281 // Without settings
282 ($($route:expr),* $(,)*) => {{
283 let settings = rocket_autodocu::settings::OpenApiSettings::new();
284 rocket_autodocu::openapi_get_spec![settings: $($route),*]
285 }};
286}