mirror of https://github.com/rwf2/Rocket.git
307 lines
9.1 KiB
Rust
307 lines
9.1 KiB
Rust
//! # Rocket - Code Generation
|
|
//!
|
|
//! This crate implements the code generation portions of Rocket. This includes
|
|
//! custom derives, custom attributes, and procedural macros. The documentation
|
|
//! here is purely technical. The code generation facilities are documented
|
|
//! 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**
|
|
//! * **error**
|
|
//!
|
|
//! The grammar for all _route_ attributes, including **route**, **get**,
|
|
//! **put**, **post**, **delete**, **head**, **patch**, and **options** is
|
|
//! defined as:
|
|
//!
|
|
//! <pre>
|
|
//! 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)
|
|
//! </pre>
|
|
//!
|
|
//! Note that the **route** attribute takes a method as its first argument,
|
|
//! while the remaining do not. That is, **route** looks like:
|
|
//!
|
|
//! #[route(GET, path = "/hello")]
|
|
//!
|
|
//! while the equivalent using **get** looks like:
|
|
//!
|
|
//! #[get("/hello")]
|
|
//!
|
|
//! The syntax for the **error** attribute is:
|
|
//!
|
|
//! <pre>
|
|
//! error := INTEGER
|
|
//! </pre>
|
|
//!
|
|
//! A use of the `error` attribute looks like:
|
|
//!
|
|
//! #[error(404)]
|
|
//!
|
|
//! ## Custom Derives
|
|
//!
|
|
//! This crate implements the following custom derives:
|
|
//!
|
|
//! * **FromForm**
|
|
//!
|
|
//! ### `FromForm`
|
|
//!
|
|
//! The [`FromForm`] derive can be applied to structures with named fields:
|
|
//!
|
|
//! #[derive(FromForm)]
|
|
//! struct MyStruct {
|
|
//! field: usize,
|
|
//! other: String
|
|
//! }
|
|
//!
|
|
//! Each field's type is required to implement [`FromFormValue`]. The derive
|
|
//! accepts one field attribute: `form`, with the following syntax:
|
|
//!
|
|
//! <pre>
|
|
//! form := 'field' '=' '"' IDENT '"'
|
|
//!
|
|
//! IDENT := valid identifier, as defined by Rust
|
|
//! </pre>
|
|
//!
|
|
//! When applied, the attribute looks as follows:
|
|
//!
|
|
//! #[derive(FromForm)]
|
|
//! struct MyStruct {
|
|
//! field: usize,
|
|
//! #[form(field = "renamed_field")]
|
|
//! other: String
|
|
//! }
|
|
//!
|
|
//! The derive generates an implementation for the [`FromForm`] trait. The
|
|
//! implementation parses a form whose field names match the field names of the
|
|
//! structure on which the derive was applied. Each field's value is parsed with
|
|
//! the [`FromFormValue`] implementation of the field's type. The `FromForm`
|
|
//! implementation succeeds only when all of the field parses succeed.
|
|
//!
|
|
//! The `form` field attribute can be used to direct that a different incoming
|
|
//! field name is expected. In this case, the attribute's field name is used
|
|
//! instead of the structure's field name when parsing a form.
|
|
//!
|
|
//! [`FromForm`]: /rocket/request/trait.FromForm.html
|
|
//! [`FromFormValue`]: /rocket/request/trait.FromFormValue.html
|
|
//!
|
|
//! ## Procedural Macros
|
|
//!
|
|
//! This crate implements the following procedural macros:
|
|
//!
|
|
//! * **routes**
|
|
//! * **errors**
|
|
//! * **uri**
|
|
//!
|
|
//! The syntax for `routes!` and `errors!` is defined as:
|
|
//!
|
|
//! <pre>
|
|
//! macro := PATH (',' PATH)*
|
|
//!
|
|
//! PATH := a path, as defined by Rust
|
|
//! </pre>
|
|
//!
|
|
//! ### Typed URIs: `uri!`
|
|
//!
|
|
//! The `uri!` macro creates a type-safe URI given a route and values for the
|
|
//! route's URI parameters.
|
|
//!
|
|
//! For example, for the following route:
|
|
//!
|
|
//! ```rust,ignore
|
|
//! #[get("/person/<name>/<age>")]
|
|
//! fn person(name: String, age: u8) -> String {
|
|
//! format!("Hello {}! You're {} years old.", name, age)
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! A URI can be created as follows:
|
|
//!
|
|
//! ```rust,ignore
|
|
//! // with unnamed parameters
|
|
//! let mike = uri!(person: "Mike", 28);
|
|
//!
|
|
//! // with named parameters
|
|
//! let mike = uri!(person: name = "Mike", age = 28);
|
|
//! let mike = uri!(person: age = 28, name = "Mike");
|
|
//!
|
|
//! // with a specific mount-point
|
|
//! let mike = uri!("/api", person: name = "Mike", age = 28);
|
|
//! ```
|
|
//!
|
|
//! #### Grammar
|
|
//!
|
|
//! The grammar for the `uri!` macro is as follows:
|
|
//!
|
|
//! <pre>
|
|
//! uri := (mount ',')? PATH (':' params)?
|
|
//!
|
|
//! mount = STRING
|
|
//! params := unnamed | named
|
|
//! unnamed := EXPR (',' EXPR)*
|
|
//! named := IDENT = EXPR (',' named)?
|
|
//!
|
|
//! EXPR := a valid Rust expression (examples: `foo()`, `12`, `"hey"`)
|
|
//! IDENT := a valid Rust identifier (examples: `name`, `age`)
|
|
//! STRING := an uncooked string literal, as defined by Rust (example: `"hi"`)
|
|
//! PATH := a path, as defined by Rust (examples: `route`, `my_mod::route`)
|
|
//! </pre>
|
|
//!
|
|
//! #### Semantics
|
|
//!
|
|
//! The `uri!` macro returns a `Uri` structure with the URI of the supplied
|
|
//! route with the given values. A `uri!` invocation only succeeds if the type
|
|
//! of every value in the invocation matches the type declared for the parameter
|
|
//! in the given route.
|
|
//!
|
|
//! The [`FromUriParam`] trait is used to typecheck and perform a conversion for
|
|
//! each value. If a `FromUriParam<S>` implementation exists for a type `T`,
|
|
//! then a value of type `S` can be used in `uri!` macro for a route URI
|
|
//! parameter declared with a type of `T`. For example, the following
|
|
//! implementation, provided by Rocket, allows an `&str` to be used in a `uri!`
|
|
//! invocation for route URI parameters declared as `String`:
|
|
//!
|
|
//! ```
|
|
//! impl<'a> FromUriParam<&'a str> for String
|
|
//! ```
|
|
//!
|
|
//! Each value passed into `uri!` is rendered in its appropriate place in the
|
|
//! URI using the [`UriDisplay`] implementation for the value's type. The
|
|
//! `UriDisplay` implementation ensures that the rendered value is URI-safe.
|
|
//!
|
|
//! If a mount-point is provided, the mount-point is prepended to the route's
|
|
//! URI.
|
|
//!
|
|
//! [`Uri`]: /rocket/http/uri/struct.URI.html
|
|
//! [`FromUriParam`]: /rocket/http/uri/trait.FromUriParam.html
|
|
//! [`UriDisplay`]: /rocket/http/uri/trait.UriDisplay.html
|
|
//!
|
|
//! # Debugging Codegen
|
|
//!
|
|
//! When the `ROCKET_CODEGEN_DEBUG` environment variable is set, this crate logs
|
|
//! the items it has generated to the console at compile-time. For example, you
|
|
//! might run the following to build a Rocket application with codegen logging
|
|
//! enabled:
|
|
//!
|
|
//! ```
|
|
//! ROCKET_CODEGEN_DEBUG=1 cargo build
|
|
//! ```
|
|
|
|
#![crate_type = "dylib"]
|
|
#![feature(quote, concat_idents, plugin_registrar, rustc_private)]
|
|
#![feature(iterator_for_each)]
|
|
#![feature(custom_attribute)]
|
|
#![feature(i128_type)]
|
|
#![allow(unused_attributes)]
|
|
#![allow(deprecated)]
|
|
|
|
#[macro_use] extern crate log;
|
|
extern crate syntax;
|
|
extern crate syntax_ext;
|
|
extern crate rustc_plugin;
|
|
extern crate rocket;
|
|
extern crate ordermap;
|
|
|
|
#[macro_use] mod utils;
|
|
mod parser;
|
|
mod macros;
|
|
mod decorators;
|
|
|
|
use std::env;
|
|
use rustc_plugin::Registry;
|
|
use syntax::ext::base::SyntaxExtension;
|
|
use syntax::symbol::Symbol;
|
|
|
|
const DEBUG_ENV_VAR: &'static str = "ROCKET_CODEGEN_DEBUG";
|
|
|
|
const PARAM_PREFIX: &'static str = "rocket_param_";
|
|
const ROUTE_STRUCT_PREFIX: &'static str = "static_rocket_route_info_for_";
|
|
const CATCH_STRUCT_PREFIX: &'static str = "static_rocket_catch_info_for_";
|
|
const ROUTE_FN_PREFIX: &'static str = "rocket_route_fn_";
|
|
const CATCH_FN_PREFIX: &'static str = "rocket_catch_fn_";
|
|
const URI_INFO_MACRO_PREFIX: &'static str = "rocket_uri_for_";
|
|
|
|
const ROUTE_ATTR: &'static str = "rocket_route";
|
|
const ROUTE_INFO_ATTR: &'static str = "rocket_route_info";
|
|
|
|
const CATCHER_ATTR: &'static str = "rocket_catcher";
|
|
|
|
macro_rules! register_decorators {
|
|
($registry:expr, $($name:expr => $func:ident),+) => (
|
|
$($registry.register_syntax_extension(Symbol::intern($name),
|
|
SyntaxExtension::MultiModifier(Box::new(decorators::$func)));
|
|
)+
|
|
)
|
|
}
|
|
|
|
macro_rules! register_derives {
|
|
($registry:expr, $($name:expr => $func:ident),+) => (
|
|
$($registry.register_custom_derive(Symbol::intern($name),
|
|
SyntaxExtension::MultiDecorator(Box::new(decorators::$func)));
|
|
)+
|
|
)
|
|
}
|
|
|
|
macro_rules! register_macros {
|
|
($reg:expr, $($n:expr => $f:ident),+) => (
|
|
$($reg.register_macro($n, macros::$f);)+
|
|
)
|
|
}
|
|
|
|
/// Compiler hook for Rust to register plugins.
|
|
#[plugin_registrar]
|
|
pub fn plugin_registrar(reg: &mut Registry) {
|
|
// Enable logging early if the DEBUG_ENV_VAR is set.
|
|
if env::var(DEBUG_ENV_VAR).is_ok() {
|
|
::rocket::logger::init(::rocket::config::LoggingLevel::Debug);
|
|
}
|
|
|
|
register_macros!(reg,
|
|
"routes" => routes,
|
|
"errors" => errors,
|
|
"uri" => uri,
|
|
"rocket_internal_uri" => uri_internal
|
|
);
|
|
|
|
register_derives!(reg,
|
|
"derive_FromForm" => from_form_derive
|
|
);
|
|
|
|
register_decorators!(reg,
|
|
"error" => error_decorator,
|
|
"route" => route_decorator,
|
|
"get" => get_decorator,
|
|
"put" => put_decorator,
|
|
"post" => post_decorator,
|
|
"delete" => delete_decorator,
|
|
"head" => head_decorator,
|
|
"patch" => patch_decorator,
|
|
"options" => options_decorator
|
|
);
|
|
}
|