Rocket

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit ea158d4344f7c8159b58f33f8172450df13703ca
parent ad0ba0d5f1eba02fe09246ab2e1afee350857cf5
Author: Sergio Benitez <sb@sergio.bz>
Date:   Mon, 15 Oct 2018 15:27:47 -0700

Individually document proc bang macros and derives.

Diffstat:
Mcore/codegen_next/src/lib.rs | 763+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Mcore/http/src/uri/uri_display.rs | 35++++++++++++++++++++++-------------
2 files changed, 456 insertions(+), 342 deletions(-)

diff --git a/core/codegen_next/src/lib.rs b/core/codegen_next/src/lib.rs @@ -15,20 +15,6 @@ //! here is purely technical. The code generation facilities are documented //! thoroughly in the [Rocket programming guide](https://rocket.rs/guide). //! -//! ## **Table of Contents** -//! -//! 1. [Custom Attributes](#custom-attributes) -//! * [`route`, `get`, `put`, ...](#route-attributes) -//! * [`catch`](#catch-attribute) -//! 2. [Custom Derives](#custom-derives) -//! * [`FromForm`](#fromform) -//! * [`FromFormValue`](#fromformvalue) -//! * [`Responder`](#responder) -//! 3. [Procedural Macros](#procedural-macros) -//! * [`routes`, `catchers`](#routes-and-catchers) -//! * [`uri`](#typed-uris-uri) -//! 4. [Debugging Generated Code](#debugging-codegen) -//! //! ## Custom Attributes //! //! This crate implements the following custom attributes: @@ -97,321 +83,6 @@ //! #[catch(404)] //! ``` //! -//! ## Custom Derives -//! -//! This crate* implements the following custom derives: -//! -//! * **FromForm** -//! * **FromFormValue** -//! * **Responder** -//! -//! <small> -//! * In reality, all of these custom derives are currently implemented by the -//! `rocket_codegen_next` crate. Nonetheless, they are documented here. -//! </small> -//! -//! ### `FromForm` -//! -//! The [`FromForm`] derive can be applied to structures with named fields: -//! -//! ```rust,ignore -//! #[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: -//! -//! ```text -//! form := 'field' '=' '"' IDENT '"' -//! -//! IDENT := valid identifier, as defined by Rust -//! ``` -//! -//! When applied, the attribute looks as follows: -//! -//! ```rust,ignore -//! #[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 `field` name in the attribute is -//! used instead of the structure's actual field name when parsing a form. -//! -//! [`FromForm`]: rocket::request::FromForm -//! [`FromFormValue`]: rocket::request::FromFormValue -//! -//! ### `FromFormValue` -//! -//! The [`FromFormValue`] derive can be applied to enums with nullary -//! (zero-length) fields: -//! -//! ```rust,ignore -//! #[derive(FromFormValue)] -//! enum MyValue { -//! First, -//! Second, -//! Third, -//! } -//! ``` -//! -//! The derive generates an implementation of the [`FromFormValue`] trait for -//! the decorated `enum`. The implementation returns successfully when the form -//! value matches, case insensitively, the stringified version of a variant's -//! name, returning an instance of said variant. -//! -//! As an example, for the `enum` above, the form values `"first"`, `"FIRST"`, -//! `"fiRSt"`, and so on would parse as `MyValue::First`, while `"second"` and -//! `"third"` would parse as `MyValue::Second` and `MyValue::Third`, -//! respectively. -//! -//! The `form` field attribute can be used to change the string that is compared -//! against for a given variant: -//! -//! ```rust,ignore -//! #[derive(FromFormValue)] -//! enum MyValue { -//! First, -//! Second, -//! #[form(value = "fourth")] -//! Third, -//! } -//! ``` -//! -//! The attribute's grammar is: -//! -//! ```text -//! form := 'field' '=' STRING_LIT -//! -//! STRING_LIT := any valid string literal, as defined by Rust -//! ``` -//! -//! The attribute accepts a single string parameter of name `value` -//! corresponding to the string to use to match against for the decorated -//! variant. In the example above, the the strings `"fourth"`, `"FOUrth"` and so -//! on would parse as `MyValue::Third`. -//! -//! ## `Responder` -//! -//! The [`Responder`] derive can be applied to enums and named structs. When -//! applied to enums, variants must have at least one field. When applied to -//! structs, the struct must have at least one field. -//! -//! ```rust,ignore -//! #[derive(Responder)] -//! enum MyResponder { -//! A(String), -//! B(OtherResponse, ContentType), -//! } -//! -//! #[derive(Responder)] -//! struct MyResponder { -//! inner: OtherResponder, -//! header: ContentType, -//! } -//! ``` -//! -//! The derive generates an implementation of the [`Responder`] trait for the -//! decorated enum or structure. The derive uses the _first_ field of a variant -//! or structure to generate a `Response`. As such, the type of the first field -//! must implement [`Responder`]. The remaining fields of a variant or structure -//! are set as headers in the produced [`Response`] using -//! [`Response::set_header()`]. As such, every other field (unless explicitly -//! ignored, explained next) must implement `Into<Header>`. -//! -//! Except for the first field, fields decorated with `#[response(ignore)]` are -//! ignored by the derive: -//! -//! ```rust,ignore -//! #[derive(Responder)] -//! enum MyResponder { -//! A(String), -//! B(OtherResponse, ContentType, #[response(ignore)] Other), -//! } -//! -//! #[derive(Responder)] -//! struct MyResponder { -//! inner: InnerResponder, -//! header: ContentType, -//! #[response(ignore)] -//! other: Other, -//! } -//! ``` -//! -//! Decorating the first field with `#[response(ignore)]` has no effect. -//! -//! Additionally, the `response` attribute can be used on named structures and -//! enum variants to override the status and/or content-type of the [`Response`] -//! produced by the generated implementation. The `response` attribute used in -//! these positions has the following grammar: -//! -//! ```text -//! response := parameter (',' parameter)? -//! -//! parameter := 'status' '=' STATUS -//! | 'content_type' '=' CONTENT_TYPE -//! -//! STATUS := unsigned integer >= 100 and < 600 -//! CONTENT_TYPE := string literal, as defined by Rust, identifying a valid -//! Content-Type, as defined by Rocket -//! ``` -//! -//! It can be used as follows: -//! -//! ```rust,ignore -//! #[derive(Responder)] -//! enum Error { -//! #[response(status = 500, content_type = "json")] -//! A(String), -//! #[response(status = 404)] -//! B(OtherResponse, ContentType), -//! } -//! -//! #[derive(Responder)] -//! #[response(status = 400)] -//! struct MyResponder { -//! inner: InnerResponder, -//! header: ContentType, -//! #[response(ignore)] -//! other: Other, -//! } -//! ``` -//! -//! The attribute accepts two key/value pairs: `status` and `content_type`. The -//! value of `status` must be an unsigned integer representing a valid status -//! code. The [`Response`] produced from the generated implementation will have -//! its status overriden to this value. -//! -//! The value of `content_type` must be a valid media-type in `top/sub` form or -//! `shorthand` form. Examples include: -//! -//! * `"text/html"` -//! * `"application/x-custom"` -//! * `"html"` -//! * `"json"` -//! * `"plain"` -//! * `"binary"` -//! -//! The [`Response`] produced from the generated implementation will have its -//! content-type overriden to this value. -//! -//! [`Responder`]: rocket::response::Responder -//! [`Response`]: rocket::Response -//! [`Response::set_header()`]: rocket::Response::set_header() -//! -//! ## Procedural Macros -//! -//! This crate implements the following procedural macros: -//! -//! * **routes** -//! * **catchers** -//! * **uri** -//! -//! ## Routes and Catchers -//! -//! The syntax for `routes!` and `catchers!` is defined as: -//! -//! ```text -//! macro := PATH (',' PATH)* -//! -//! PATH := a path, as defined by Rust -//! ``` -//! -//! ### Typed URIs: `uri!` -//! -//! The `uri!` macro creates a type-safe URI given a route and values for the -//! route's URI parameters. The inputs to the macro are the path to a route, a -//! colon, and one argument for each dynamic parameter (parameters in `<>`) in -//! the route's path. -//! -//! 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, in route path declaration order -//! let mike = uri!(person: "Mike", 28); -//! -//! // with named parameters, order irrelevant -//! 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: -//! -//! ```text -//! 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`) -//! ``` -//! -//! #### Semantics -//! -//! The `uri!` macro returns an [`Origin`](rocket::uri::Origin) structure with -//! the URI of the supplied route interpolated with the given values. Note that -//! `Origin` implements `Into<Uri>` (and by extension, `TryInto<Uri>`), so it -//! can be converted into a [`Uri`](rocket::uri::Uri) using `.into()` as needed. -//! -//! -//! A `uri!` invocation only typechecks 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`: -//! -//! ```rust,ignore -//! 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`]: http::uri::URI -//! [`FromUriParam`]: http::uri::FromUriParam -//! [`UriDisplay`]: http::uri::UriDisplay -//! //! # Debugging Codegen //! //! When the `ROCKET_CODEGEN_DEBUG` environment variable is set, this crate logs @@ -481,16 +152,257 @@ route_attribute!(head => Method::Head); route_attribute!(patch => Method::Patch); route_attribute!(options => Method::Options); +/// Derive for the [`FromFormValue`] trait. +/// +/// The [`FromFormValue`] derive can be applied to enums with nullary +/// (zero-length) fields: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # +/// #[derive(FromFormValue)] +/// enum MyValue { +/// First, +/// Second, +/// Third, +/// } +/// ``` +/// +/// The derive generates an implementation of the [`FromFormValue`] trait for +/// the decorated `enum`. The implementation returns successfully when the form +/// value matches, case insensitively, the stringified version of a variant's +/// name, returning an instance of said variant. If there is no match, an error +/// ([`FromFormValue::Error`]) of type [`&RawStr`] is returned, the value of +/// which is the raw form field value that failed to match. +/// +/// As an example, for the `enum` above, the form values `"first"`, `"FIRST"`, +/// `"fiRSt"`, and so on would parse as `MyValue::First`, while `"second"` and +/// `"third"` would parse as `MyValue::Second` and `MyValue::Third`, +/// respectively. +/// +/// The `form` field attribute can be used to change the string that is compared +/// against for a given variant: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # +/// #[derive(FromFormValue)] +/// enum MyValue { +/// First, +/// Second, +/// #[form(value = "fourth")] +/// Third, +/// } +/// ``` +/// +/// The `#[form]` attribute's grammar is: +/// +/// ```text +/// form := 'field' '=' STRING_LIT +/// +/// STRING_LIT := any valid string literal, as defined by Rust +/// ``` +/// +/// The attribute accepts a single string parameter of name `value` +/// corresponding to the string to use to match against for the decorated +/// variant. In the example above, the the strings `"fourth"`, `"FOUrth"` and so +/// on would parse as `MyValue::Third`. +/// +/// [`FromFormValue`]: ../rocket/request/trait.FromFormValue.html +/// [`FromFormValue::Error`]: ../rocket/request/trait.FromFormValue.html#associatedtype.Error +/// [`&RawStr`]: ../rocket/http/struct.RawStr.html +// FIXME(rustdoc): We should be able to refer to items in `rocket`. #[proc_macro_derive(FromFormValue, attributes(form))] pub fn derive_from_form_value(input: TokenStream) -> TokenStream { emit!(derive::from_form_value::derive_from_form_value(input)) } +/// Derive for the [`FromForm`] trait. +/// +/// The [`FromForm`] derive can be applied to structures with named fields: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # +/// #[derive(FromForm)] +/// struct MyStruct { +/// field: usize, +/// other: String +/// } +/// ``` +/// +/// Each field's type is required to implement [`FromFormValue`]. +/// +/// The derive generates an implementation of 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. If +/// parsing fails, an error ([`FromForm::Error`]) of type [`FormParseError`] is +/// returned. +/// +/// The derive accepts one field attribute: `form`, with the following syntax: +/// +/// ```text +/// form := 'field' '=' '"' IDENT '"' +/// +/// IDENT := valid identifier, as defined by Rust +/// ``` +/// +/// When applied, the attribute looks as follows: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # +/// #[derive(FromForm)] +/// struct MyStruct { +/// field: usize, +/// #[form(field = "renamed_field")] +/// other: String +/// } +/// ``` +/// +/// The field attribute directs that a different incoming field name is +/// expected, and the value of the `field` attribute is used instead of the +/// structure's actual field name when parsing a form. In the example above, the +/// value of the `MyStruct::other` struct field will be parsed from the incoming +/// form's `renamed_field` field. +/// +/// [`FromForm`]: ../rocket/request/trait.FromForm.html +/// [`FromFormValue`]: ../rocket/request/trait.FromFormValue.html +/// [`FormParseError`]: ../rocket/request/enum.FormParseError.html +/// [`FromForm::Error`]: ../rocket/request/trait.FromForm.html#associatedtype.Error #[proc_macro_derive(FromForm, attributes(form))] pub fn derive_from_form(input: TokenStream) -> TokenStream { emit!(derive::from_form::derive_from_form(input)) } +/// Derive for the [`Responder`] trait. +/// +/// The [`Responder`] derive can be applied to enums and structs with named +/// fields. When applied to enums, variants must have at least one field. When +/// applied to structs, the struct must have at least one field. +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # use std::fs::File; +/// # use rocket::http::ContentType; +/// # type OtherResponder = MyResponderA; +/// # +/// #[derive(Responder)] +/// enum MyResponderA { +/// A(String), +/// B(File, ContentType), +/// } +/// +/// #[derive(Responder)] +/// struct MyResponderB { +/// inner: OtherResponder, +/// header: ContentType, +/// } +/// ``` +/// +/// The derive generates an implementation of the [`Responder`] trait for the +/// decorated enum or structure. The derive uses the _first_ field of a variant +/// or structure to generate a [`Response`]. As such, the type of the first +/// field must implement [`Responder`]. The remaining fields of a variant or +/// structure are set as headers in the produced [`Response`] using +/// [`Response::set_header()`]. As such, every other field (unless explicitly +/// ignored, explained next) must implement `Into<Header>`. +/// +/// Except for the first field, fields decorated with `#[response(ignore)]` are +/// ignored by the derive: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # use std::fs::File; +/// # use rocket::http::ContentType; +/// # use rocket::response::NamedFile; +/// # type Other = usize; +/// # +/// #[derive(Responder)] +/// enum MyResponder { +/// A(String), +/// B(File, ContentType, #[response(ignore)] Other), +/// } +/// +/// #[derive(Responder)] +/// struct MyOtherResponder { +/// inner: NamedFile, +/// header: ContentType, +/// #[response(ignore)] +/// other: Other, +/// } +/// ``` +/// +/// Decorating the first field with `#[response(ignore)]` has no effect. +/// +/// Additionally, the `response` attribute can be used on named structures and +/// enum variants to override the status and/or content-type of the [`Response`] +/// produced by the generated implementation. The `response` attribute used in +/// these positions has the following grammar: +/// +/// ```text +/// response := parameter (',' parameter)? +/// +/// parameter := 'status' '=' STATUS +/// | 'content_type' '=' CONTENT_TYPE +/// +/// STATUS := unsigned integer >= 100 and < 600 +/// CONTENT_TYPE := string literal, as defined by Rust, identifying a valid +/// Content-Type, as defined by Rocket +/// ``` +/// +/// It can be used as follows: +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// # use rocket::http::ContentType; +/// # use rocket::response::NamedFile; +/// # type Other = usize; +/// # type InnerResponder = String; +/// # +/// #[derive(Responder)] +/// enum Error { +/// #[response(status = 500, content_type = "json")] +/// A(String), +/// #[response(status = 404)] +/// B(NamedFile, ContentType), +/// } +/// +/// #[derive(Responder)] +/// #[response(status = 400)] +/// struct MyResponder { +/// inner: InnerResponder, +/// header: ContentType, +/// #[response(ignore)] +/// other: Other, +/// } +/// ``` +/// +/// The attribute accepts two key/value pairs: `status` and `content_type`. The +/// value of `status` must be an unsigned integer representing a valid status +/// code. The [`Response`] produced from the generated implementation will have +/// its status overriden to this value. +/// +/// The value of `content_type` must be a valid media-type in `top/sub` form or +/// `shorthand` form. Examples include: +/// +/// * `"text/html"` +/// * `"application/x-custom"` +/// * `"html"` +/// * `"json"` +/// * `"plain"` +/// * `"binary"` +/// +/// See [`ContentType::parse_flexible()`] for a full list of available +/// shorthands. The [`Response`] produced from the generated implementation will +/// have its content-type overriden to this value. +/// +/// [`Responder`]: ../rocket/response/trait.Responder.html +/// [`Response`]: ../rocket/struct.Response.html +/// [`Response::set_header()`]: ../rocket/response/struct.Response.html#method.set_header +/// [`ContentType::parse_flexible()`]: ../rocket/http/struct.ContentType.html#method.parse_flexible #[proc_macro_derive(Responder, attributes(response))] pub fn derive_responder(input: TokenStream) -> TokenStream { emit!(derive::responder::derive_responder(input)) @@ -501,16 +413,209 @@ 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. +/// +/// The `routes!` macro expands a list of route paths into a [`Vec`] of their +/// corresponding [`Route`] structures. For example, given the following routes: +/// +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// #[get("/")] +/// fn index() { /* .. */ } +/// +/// mod person { +/// #[post("/hi/<person>")] +/// pub fn hello(person: String) { /* .. */ } +/// } +/// ``` +/// +/// The `routes!` macro can be used as: +/// +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// # use rocket::http::Method; +/// # +/// # #[get("/")] fn index() { /* .. */ } +/// # mod person { +/// # #[post("/hi/<person>")] pub fn hello(person: String) { /* .. */ } +/// # } +/// let my_routes = routes![index, person::hello]; +/// assert_eq!(my_routes.len(), 2); +/// +/// let index_route = &my_routes[0]; +/// assert_eq!(index_route.method, Method::Get); +/// assert_eq!(index_route.name, Some("index")); +/// assert_eq!(index_route.uri.path(), "/"); +/// +/// let hello_route = &my_routes[1]; +/// assert_eq!(hello_route.method, Method::Post); +/// assert_eq!(hello_route.name, Some("hello")); +/// assert_eq!(hello_route.uri.path(), "/hi/<person>"); +/// ``` +/// +/// The grammar for `routes!` is defined as: +/// +/// ```text +/// routes := PATH (',' PATH)* +/// +/// PATH := a path, as defined by Rust +/// ``` +/// +/// [`Route`]: ../rocket/struct.Route.html #[proc_macro] pub fn routes(input: TokenStream) -> TokenStream { emit!(bang::routes_macro(input)) } +/// Generates a [`Vec`] of [`Catcher`]s from a set of catcher paths. +/// +/// The `catchers!` macro expands a list of catcher paths into a [`Vec`] of +/// their corresponding [`Catcher`] structures. For example, given the following +/// catchers: +/// +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// #[catch(404)] +/// fn not_found() { /* .. */ } +/// +/// mod inner { +/// #[catch(400)] +/// pub fn unauthorized() { /* .. */ } +/// } +/// ``` +/// +/// The `catchers!` macro can be used as: +/// +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// # #[catch(404)] fn not_found() { /* .. */ } +/// # mod inner { +/// # #[catch(401)] pub fn unauthorized() { /* .. */ } +/// # } +/// # +/// let my_catchers = catchers![not_found, inner::unauthorized]; +/// assert_eq!(my_catchers.len(), 2); +/// +/// let not_found = &my_catchers[0]; +/// assert_eq!(not_found.code, 404); +/// +/// let unauthorized = &my_catchers[1]; +/// assert_eq!(unauthorized.code, 401); +/// ``` +/// +/// The grammar for `catchers!` is defined as: +/// +/// ```text +/// catchers := PATH (',' PATH)* +/// +/// PATH := a path, as defined by Rust +/// ``` +/// +/// [`Catcher`]: ../rocket/struct.Catcher.html #[proc_macro] pub fn catchers(input: TokenStream) -> TokenStream { emit!(bang::catchers_macro(input)) } +/// Type safe generation of route URIs. +/// +/// The `uri!` macro creates a type-safe, URL safe URI given a route and values +/// for the route's URI parameters. The inputs to the macro are the path to a +/// route, a colon, and one argument for each dynamic parameter (parameters in +/// `<>`) in the route's path and query. +/// +/// For example, for the following route: +/// +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// #[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 +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # +/// # #[get("/person/<name>/<age>")] +/// # fn person(name: String, age: u8) { } +/// # +/// // with unnamed parameters, in route path declaration order +/// let mike = uri!(person: "Mike Smith", 28); +/// assert_eq!(mike.path(), "/person/Mike%20Smith/28"); +/// +/// // with named parameters, order irrelevant +/// let mike = uri!(person: name = "Mike", age = 28); +/// let mike = uri!(person: age = 28, name = "Mike"); +/// assert_eq!(mike.path(), "/person/Mike/28"); +/// +/// // with a specific mount-point +/// let mike = uri!("/api", person: name = "Mike", age = 28); +/// assert_eq!(mike.path(), "/api/person/Mike/28"); +/// ``` +/// +/// ## Grammar +/// +/// The grammar for the `uri!` macro is: +/// +/// ```text +/// 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`) +/// ``` +/// +/// ## Semantics +/// +/// The `uri!` macro returns an [`Origin`] structure with the URI of the +/// supplied route interpolated with the given values. Note that `Origin` +/// implements `Into<Uri>` (and by extension, `TryInto<Uri>`), so it can be +/// converted into a [`Uri`] using `.into()` as needed. +/// +/// A `uri!` invocation only typechecks 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`: +/// +/// ```rust,ignore +/// 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/enum.Uri.html +/// [`Origin`]: ../rocket/http/uri/struct.Origin.html +/// [`FromUriParam`]: ../rocket/http/uri/trait.FromUriParam.html +/// [`UriDisplay`]: ../rocket/http/uri/trait.UriDisplay.html #[proc_macro] pub fn uri(input: TokenStream) -> TokenStream { emit!(bang::uri_macro(input)) diff --git a/core/http/src/uri/uri_display.rs b/core/http/src/uri/uri_display.rs @@ -29,32 +29,41 @@ use self::priv_encode_set::PATH_ENCODE_SET; /// implementation for these types is used when generating the URI. /// /// To illustrate `UriDisplay`'s role in code generation for `uri!`, consider -/// the following fictional route and struct definition: +/// the following route: /// -/// ```rust,ignore -/// struct Value { .. }; -/// -/// #[get("/item/<id>/<value>")] -/// fn get_item(id: i32, value: Value) -> T { .. } +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # type T = (); +/// #[get("/item/<id>?<track>")] +/// fn get_item(id: i32, track: String) -> T { /* .. */ } /// ``` /// /// A URI for this route can be generated as follows: /// -/// ```rust,ignore +/// ```rust +/// # #![feature(proc_macro_hygiene, decl_macro)] +/// # #[macro_use] extern crate rocket; +/// # type T = (); +/// # #[get("/item/<id>?<track>")] +/// # fn get_item(id: i32, track: String) -> T { /* .. */ } +/// # /// // With unnamed parameters. -/// uri!(get_item: 100, Value { .. }); +/// uri!(get_item: 100, "inbound"); /// /// // With named parameters. -/// uri!(get_item: id = 100, value = Value { .. }); +/// uri!(get_item: id = 100, track = "inbound"); +/// uri!(get_item: track = "inbound", id = 100); /// ``` /// /// After verifying parameters and their types, Rocket will generate code /// similar to the following: /// -/// ```rust,ignore -/// format!("/item/{id}/{value}", -/// id = &100 as &UriDisplay, -/// value = &Value { .. } as &UriDisplay); +/// ```rust +/// # extern crate rocket; +/// # use rocket::http::uri::UriDisplay; +/// # +/// format!("/item/{}?track={}", &100 as &UriDisplay, &"inbound" as &UriDisplay); /// ``` /// /// For this expression to typecheck, both `i32` and `Value` must implement