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![$($route),*](&$settings);
206        let routes = rocket_autodocu::openapi_routes![$($route),*](Some(spec), &$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![$($route),*](&$settings);
245        let routes = rocket_autodocu::openapi_routes![$($route),*](None, &$settings);
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![$($route),*](&$settings);
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}