mirror of https://github.com/rwf2/Rocket.git
Individually document proc bang macros and derives.
This commit is contained in:
parent
ad0ba0d5f1
commit
ea158d4344
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue