rocket_autodocu_codegen/lib.rs
1#![forbid(unsafe_code)]
2#![deny(clippy::all)]
3
4//! This crate is used by [`rocket_autodocu`](https://crates.io/crates/rocket_autodocu)
5//! for code generation. This crate includes the procedural macros like:
6//! - `#[openapi]`: To generate the documentation for an endpoint/route.
7//! - `openapi_routes![...]`: Returns a closure for generating routes.
8//! - `openapi_spec![...]`: Returns a closure for generating OpenApi objects.
9//! - `#[derive(OpenApiFromRequest)]`: Implement `OpenApiFromRequest` trait for a given struct.
10//!
11
12mod openapi_attr;
13mod openapi_spec;
14mod parse_routes;
15
16use proc_macro::TokenStream;
17use quote::quote;
18use syn::Ident;
19
20/// A proc macro to be used in tandem with one of `Rocket`'s endpoint macros. It requires that all
21/// of the arguments of the route implement one of the traits in `rocket_autodocu::request`, and that
22/// the return type implements `OpenApiResponder`.
23/// ### Example
24/// ```rust,ignore
25/// use rocket_autodocu::openapi;
26/// use rocket::get;
27///
28/// #[openapi]
29/// #[get("/hello/<number>")]
30/// fn hello_world(number: i32) -> String {
31/// format!("Hello world number {}", number)
32/// }
33/// ```
34#[proc_macro_attribute]
35pub fn openapi(args: TokenStream, mut input: TokenStream) -> TokenStream {
36 // We don't need to modify/replace the input TokenStream,
37 // we just need to append to it.
38 input.extend(openapi_attr::parse(args, input.clone()));
39 input
40}
41
42/// Generate and return a closure that can be used to generate the routes.
43///
44/// This closure take 2 arguments:
45/// - `spec_opt`: `Option<rocket_autodocu::autodocu::openapi3::OpenApi>`
46/// - `settings`: `rocket_autodocu::settings::OpenApiSettings`
47///
48/// It returns `Vec<::rocket::Route>`.
49///
50/// If `spec_opt` is set to `None` it will not add a route to serve the `openapi.json` file.
51///
52/// Example:
53/// ```rust,ignore
54/// let settings = rocket_autodocu::settings::OpenApiSettings::new();
55/// let spec = rocket_autodocu::openapi_spec);
56/// let routes = rocket_autodocu::openapi_routes, settings);
57/// ```
58#[proc_macro]
59pub fn openapi_routes(input: TokenStream) -> TokenStream {
60 let routes = parse_routes::parse_routes(input).unwrap_or_else(|e| e.to_compile_error());
61 (quote! {
62 #routes
63 })
64 .into()
65}
66
67/// Generate and return a closure that can be used to generate the OpenAPI specification.
68///
69/// This closure take 1 argument:
70/// - `settings`: `rocket_autodocu::settings::OpenApiSettings`
71///
72/// It returns `rocket_autodocu::autodocu::openapi3::OpenApi`.
73///
74/// Example:
75/// ```rust,ignore
76/// let settings = rocket_autodocu::settings::OpenApiSettings::new();
77/// let spec = rocket_autodocu::openapi_spec;
78/// ```
79#[proc_macro]
80pub fn openapi_spec(input: TokenStream) -> TokenStream {
81 let spec = openapi_spec::create_openapi_spec(input).unwrap_or_else(|e| e.to_compile_error());
82 (quote! {
83 #spec
84 })
85 .into()
86}
87
88/// Derive marco for the `OpenApiFromRequest` trait.
89///
90/// This derive trait is a very simple implementation for anything that does not
91/// require any other special headers or parameters to be validated.
92///
93/// Use:
94/// ```rust,ignore
95/// use rocket_autodocu::request::OpenApiFromRequest;
96///
97/// #[derive(OpenApiFromRequest)]
98/// pub struct MyStructName;
99/// ```
100///
101/// This code is equivalent to:
102/// ```rust,ignore
103/// use rocket_autodocu::request::{OpenApiFromRequest, RequestHeaderInput};
104/// use rocket_autodocu::gen::OpenApiGenerator;
105///
106/// pub struct MyStructName;
107///
108/// impl<'r> OpenApiFromRequest<'r> for MyStructName {
109/// fn from_request_input(
110/// _gen: &mut OpenApiGenerator,
111/// _name: String,
112/// _required: bool,
113/// ) -> rocket_autodocu::Result<RequestHeaderInput> {
114/// Ok(RequestHeaderInput::None)
115/// }
116/// }
117/// ```
118#[proc_macro_derive(OpenApiFromRequest)]
119pub fn open_api_from_request_derive(input: TokenStream) -> TokenStream {
120 // Construct a representation of Rust code as a syntax tree
121 // that we can manipulate
122 let ast: syn::DeriveInput = syn::parse(input).unwrap();
123 let name = &ast.ident;
124
125 let gen = quote! {
126 impl<'r> rocket_autodocu::request::OpenApiFromRequest<'r> for #name {
127 fn from_request_input(
128 _gen: &mut rocket_autodocu::gen::OpenApiGenerator,
129 _name: String,
130 _required: bool,
131 ) -> rocket_autodocu::Result<rocket_autodocu::request::RequestHeaderInput> {
132 Ok(rocket_autodocu::request::RequestHeaderInput::None)
133 }
134 }
135 };
136 gen.into()
137}
138
139fn get_add_operation_fn_name(route_fn_name: &Ident) -> Ident {
140 Ident::new(
141 &format!("autodocu_add_operation_for_{}_", route_fn_name),
142 route_fn_name.span(),
143 )
144}