Document codegen attributes individually.

This commit is contained in:
Sergio Benitez 2018-10-18 04:00:36 -07:00
parent 330390ad71
commit 831d8dfbe3
2 changed files with 255 additions and 85 deletions

View File

@ -326,24 +326,23 @@ fn generate_internal_uri_macro(route: &Route) -> TokenStream2 {
fn codegen_route(route: Route) -> Result<TokenStream> { fn codegen_route(route: Route) -> Result<TokenStream> {
// Generate the declarations for path, data, and request guard parameters. // Generate the declarations for path, data, and request guard parameters.
let mut data_stmt = None; let mut data_stmt = None;
let mut req_guard_definitions = vec![];
let mut parameter_definitions = vec![]; let mut parameter_definitions = vec![];
for (ident, rocket_ident, ty) in &route.inputs { for (ident, rocket_ident, ty) in &route.inputs {
let fn_segment: Segment = ident.into(); let fn_segment: Segment = ident.into();
let parameter_def = match route.segments.get(&fn_segment) { match route.segments.get(&fn_segment) {
Some(seg) if seg.source == Source::Path => { Some(seg) if seg.source == Source::Path => {
param_expr(seg, rocket_ident, &ty) parameter_definitions.push(param_expr(seg, rocket_ident, &ty));
} }
Some(seg) if seg.source == Source::Data => { Some(seg) if seg.source == Source::Data => {
// the data statement needs to come last, so record it specially // the data statement needs to come last, so record it specially
data_stmt = Some(data_expr(rocket_ident, &ty)); data_stmt = Some(data_expr(rocket_ident, &ty));
continue;
} }
// handle query parameters later Some(_) => continue, // handle query parameters later
Some(_) => continue, None => {
None => request_guard_expr(rocket_ident, &ty), req_guard_definitions.push(request_guard_expr(rocket_ident, &ty));
}
}; };
parameter_definitions.push(parameter_def);
} }
// Generate the declarations for query parameters. // Generate the declarations for query parameters.
@ -381,6 +380,7 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
request::{Query, FromQuery, FormItems, FormItem}, request::{Query, FromQuery, FormItems, FormItem},
}; };
#(#req_guard_definitions)*
#(#parameter_definitions)* #(#parameter_definitions)*
#data_stmt #data_stmt

View File

@ -15,80 +15,12 @@
//! here is purely technical. The code generation facilities are documented //! here is purely technical. The code generation facilities are documented
//! thoroughly in the [Rocket programming guide](https://rocket.rs/guide). //! thoroughly in the [Rocket programming guide](https://rocket.rs/guide).
//! //!
//! ## Custom Attributes
//!
//! This crate implements the following custom attributes:
//!
//! * **route**
//! * **get**
//! * **put**
//! * **post**
//! * **delete**
//! * **head**
//! * **patch**
//! * **options**
//! * **catch**
//!
//! ### Route Attributes
//!
//! The grammar for all _route_ attributes, including **route**, **get**,
//! **put**, **post**, **delete**, **head**, **patch**, and **options** is
//! defined as:
//!
//! ```text
//! route := METHOD? '(' ('path' '=')? path (',' kv_param)* ')'
//!
//! path := URI_SEG
//! | DYNAMIC_PARAM
//! | '?' DYNAMIC_PARAM
//! | path '/' path
//! (string literal)
//!
//! kv_param := 'rank' '=' INTEGER
//! | 'format' '=' STRING
//! | 'data' '=' DYNAMIC_PARAM
//!
//! INTEGER := isize, as defined by Rust
//! STRING := UTF-8 string literal, as defined by Rust
//! IDENT := valid identifier, as defined by Rust
//!
//! URI_SEG := valid HTTP URI Segment
//! DYNAMIC_PARAM := '<' IDENT '..'? '>' (string literal)
//! ```
//!
//! Note that the **route** attribute takes a method as its first argument,
//! while the remaining do not. That is, **route** looks like:
//!
//! ```rust,ignore
//! #[route(GET, path = "/hello")]
//! ```
//!
//! while the equivalent using **get** looks like:
//!
//! ```rust,ignore
//! #[get("/hello")]
//! ```
//!
//! ### Catch Attribute
//!
//! The syntax for the **catch** attribute is:
//!
//! ```text
//! catch := INTEGER
//! ```
//!
//! A use of the `catch` attribute looks like:
//!
//! ```rust,ignore
//! #[catch(404)]
//! ```
//!
//! # Debugging Codegen //! # Debugging Codegen
//! //!
//! When the `ROCKET_CODEGEN_DEBUG` environment variable is set, this crate logs //! When the `ROCKET_CODEGEN_DEBUG` environment variable is set, this crate
//! the items it has generated to the console at compile-time. For example, you //! logs, at compile-time and to the console, the items it generates. For
//! might run the following to build a Rocket application with codegen logging //! example, you might run the following to build a Rocket application with
//! enabled: //! codegen debug logging enabled:
//! //!
//! ```sh //! ```sh
//! ROCKET_CODEGEN_DEBUG=1 cargo build //! ROCKET_CODEGEN_DEBUG=1 cargo build
@ -136,6 +68,187 @@ macro_rules! emit {
macro_rules! route_attribute { macro_rules! route_attribute {
($name:ident => $method:expr) => ( ($name:ident => $method:expr) => (
/// Attribute to generate a [`Route`] and associated metadata.
///
/// This and all other route attributes can only be applied to free
/// functions:
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// #
/// #[get("/")]
/// fn index() -> &'static str {
/// "Hello, world!"
/// }
/// ```
///
/// There are 7 method-specific route attributes:
///
/// * [`#[get]`] - `GET` specific route
/// * [`#[put]`] - `PUT` specific route
/// * [`#[post]`] - `POST` specific route
/// * [`#[delete]`] - `DELETE` specific route
/// * [`#[head]`] - `HEAD` specific route
/// * [`#[options]`] - `OPTIONS` specific route
/// * [`#[patch]`] - `PATCH` specific route
///
/// Additionally, [`#[route]`] allows the method and path to be
/// explicitly specified:
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// #
/// #[route(GET, path = "/")]
/// fn index() -> &'static str {
/// "Hello, world!"
/// }
/// ```
///
/// [`#[delete]`]: attr.delete.html
/// [`#[get]`]: attr.get.html
/// [`#[head]`]: attr.head.html
/// [`#[options]`]: attr.options.html
/// [`#[patch]`]: attr.patch.html
/// [`#[post]`]: attr.post.html
/// [`#[put]`]: attr.put.html
/// [`#[route]`]: attr.route.html
///
/// # Grammar
///
/// The grammar for all method-specific route attributes is defined as:
///
/// ```text
/// route := '"' path ('?' query)? '"' (',' parameter)*
///
/// path := ('/' segment)*
///
/// query := segment ('&' segment)*
///
/// segment := URI_SEG
/// | SINGLE_PARAM
/// | MULTI_PARAM
///
/// parameter := 'rank' '=' INTEGER
/// | 'format' '=' '"' MEDIA_TYPE '"'
/// | 'data' '=' '"' SINGLE_PARAM '"'
///
/// SINGLE_PARAM := '<' IDENT '>'
/// MULTI_PARAM := '<' IDENT '..>'
///
/// URI_SEG := valid, non-percent-encoded HTTP URI segment
/// MEDIA_TYPE := valid HTTP media type or known shorthand
///
/// INTEGER := unsigned integer, as defined by Rust
/// IDENT := valid identifier, as defined by Rust, except `_`
/// ```
///
/// The generic route attribute is defined as:
///
/// ```text
/// generic-route := METHOD ',' path = route
/// ```
///
/// # Typing Requirements
///
/// Every identifier that appears in a dynamic parameter (`SINGLE_PARAM`
/// or `MULTI_PARAM`) must appear as an argument to the function. For
/// example, the following route requires the decorated function to have
/// the arguments `foo`, `baz`, `msg`, `rest`, and `form`:
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// # use rocket::request::Form;
/// # use std::path::PathBuf;
/// # #[derive(FromForm)] struct F { a: usize }
/// #[get("/<foo>/bar/<baz..>?<msg>&closed&<rest..>", data = "<form>")]
/// # fn f(foo: usize, baz: PathBuf, msg: String, rest: Form<F>, form: Form<F>) { }
/// ```
///
/// The type of each function argument corresponding to a dynamic
/// parameter is required to implement one of Rocket's guard traits. The
/// exact trait that is required to be implemented depends on the kind
/// of dynamic parameter (`SINGLE` or `MULTI`) and where in the route
/// attribute the parameter appears. The table below summarizes trait
/// requirements:
///
/// | position | kind | trait |
/// |----------|-------------|-------------------|
/// | path | `<ident>` | [`FromParam`] |
/// | path | `<ident..>` | [`FromSegments`] |
/// | query | `<ident>` | [`FromFormValue`] |
/// | query | `<ident..>` | [`FromQuery`] |
/// | data | `<ident>` | [`FromData`] |
///
/// The type of each function argument that _does not_ have a
/// corresponding dynamic parameter is required to implement the
/// [`FromRequest`] trait.
///
/// The return type of the decorated function must implement the
/// [`Responder`] trait.
///
/// [`FromParam`]: ../rocket/request/trait.FromParam.html
/// [`FromSegments`]: ../rocket/request/trait.FromSegments.html
/// [`FromFormValue`]: ../rocket/request/form/trait.FromFormValue.html
/// [`FromQuery`]: ../rocket/request/trait.FromQuery.html
/// [`FromData`]: ../rocket/data/trait.FromData.html
/// [`FromRequest`]: ../rocket/request/trait.FromRequest.html
/// [`Route`]: ../rocket/struct.Route.html
/// [`Responder`]: ../rocket/response/trait.Responder.html
///
/// # Semantics
///
/// The attribute generates three items:
///
/// 1. A route [`Handler`].
///
/// The generated handler validates and generates all arguments for
/// the generated function according to the trait that their type
/// must implement. The order in which arguments are processed is:
///
/// 1. Request guards from left to right.
///
/// If a request guard fails, the request is forwarded if the
/// [`Outcome`] is `Forward` or failed if the [`Outcome`] is
/// `Failure`. See [`FromRequest` Outcomes] for further
/// detail.
///
/// 2. Path and query parameters from left to right as declared
/// in the function argument list.
///
/// If a path or query parameter guard fails, the request is
/// forwarded.
///
/// 3. Data parameter, if any.
///
/// If a data guard fails, the request is forwarded if the
/// [`Outcome`] is `Forward` or failed if the [`Outcome`] is
/// `Failure`. See [`FromData` Outcomes] for further detail.
///
/// If all validation succeeds, the decorated function is called.
/// The returned value is used to generate a [`Response`] via the
/// type's [`Responder`] implementation.
///
/// 2. A static structure used by [`routes!`] to generate a [`Route`].
///
/// The static structure (and resulting [`Route`]) is populated
/// with the name (the function's name), path, query, rank, and
/// format from the route attribute. The handler is set to the
/// generated handler.
///
/// 3. A macro used by [`uri!`] to type-check and generate an
/// [`Origin`].
///
/// [`Handler`]: ../rocket/trait.Handler.html
/// [`routes!`]: macro.routes.html
/// [`uri!`]: macro.uri.html
/// [`Origin`]: ../rocket/http/uri/struct.Origin.html
/// [`Outcome`]: ../rocket/enum.Outcome.html
/// [`Response`]: ../rocket/struct.Response.html
/// [`FromRequest` Outcomes]: ../rocket/request/trait.FromRequest.html#outcomes
/// [`FromData` Outcomes]: ../rocket/data/trait.FromData.html#outcomes
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn $name(args: TokenStream, input: TokenStream) -> TokenStream { pub fn $name(args: TokenStream, input: TokenStream) -> TokenStream {
emit!(attribute::route::route_attribute($method, args, input)) emit!(attribute::route::route_attribute($method, args, input))
@ -152,6 +265,68 @@ route_attribute!(head => Method::Head);
route_attribute!(patch => Method::Patch); route_attribute!(patch => Method::Patch);
route_attribute!(options => Method::Options); route_attribute!(options => Method::Options);
/// Attribute to generate a [`Catcher`] and associated metadata.
///
/// This attribute can only be applied to free functions:
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// #
/// use rocket::Request;
///
/// #[catch(404)]
/// fn not_found(req: &Request) -> String {
/// format!("Sorry, {} does not exist.", req.uri())
/// }
/// ```
///
/// # Grammar
///
/// The grammar for the `#[catch]` attributes is defined as:
///
/// ```text
/// catch := STATUS
///
/// STATUS := valid HTTP status code (integer in [200, 599])
/// ```
///
/// # Typing Requirements
///
/// The decorated function must take exactly zero or one argument. If the
/// decorated function takes an argument, the argument's type must be
/// [`&Request`].
///
/// The return type of the decorated function must implement the [`Responder`]
/// trait.
///
/// # Semantics
///
/// The attribute generates two items:
///
/// 1. An [`ErrorHandler`].
///
/// The generated handler calls the decorated function, passing in the
/// [`&Request`] value if requested. The returned value is used to generate
/// a [`Response`] via the type's [`Responder`] implementation.
///
/// 2. A static structure used by [`catchers!`] to generate a [`Catcher`].
///
/// The static structure (and resulting [`Catcher`]) is populated
/// with the name (the function's name) and status code from the
/// route attribute. The handler is set to the generated handler.
///
/// [`&Request`]: ../rocket/struct.Request.html
/// [`ErrorHandler`]: ../rocket/type.ErrorHandler.html
/// [`catchers!`]: macro.catchers.html
/// [`Catcher`]: ../rocket/struct.Catcher.html
/// [`Response`]: ../rocket/struct.Response.html
/// [`Responder`]: ../rocket/response/trait.Responder.html
#[proc_macro_attribute]
pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream {
emit!(attribute::catch::catch_attribute(args, input))
}
/// Derive for the [`FromFormValue`] trait. /// Derive for the [`FromFormValue`] trait.
/// ///
/// The [`FromFormValue`] derive can be applied to enums with nullary /// The [`FromFormValue`] derive can be applied to enums with nullary
@ -408,11 +583,6 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
emit!(derive::responder::derive_responder(input)) emit!(derive::responder::derive_responder(input))
} }
#[proc_macro_attribute]
pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream {
emit!(attribute::catch::catch_attribute(args, input))
}
/// Generates a [`Vec`] of [`Route`]s from a set of route paths. /// Generates a [`Vec`] of [`Route`]s from a set of route paths.
/// ///
/// The `routes!` macro expands a list of route paths into a [`Vec`] of their /// The `routes!` macro expands a list of route paths into a [`Vec`] of their