Rocket

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

commit b9bf1ee37de21f90f0ed1a64cde4ed611a1325ac
parent 61f107f550d895c26eff3b8d78b6fdd0c04d9822
Author: Sergio Benitez <sb@sergio.bz>
Date:   Thu,  4 Oct 2018 03:54:46 -0700

Fix typed URI generation for query reform.

Diffstat:
Mcontrib/codegen/Cargo.toml | 2+-
Mcore/codegen/Cargo.toml | 5-----
Dcore/codegen/src/lib.rs | 387-------------------------------------------------------------------------------
Dcore/codegen/src/utils/expr_ext.rs | 18------------------
Dcore/codegen/src/utils/ident_ext.rs | 20--------------------
Dcore/codegen/src/utils/parser_ext.rs | 79-------------------------------------------------------------------------------
Dcore/codegen/src/utils/span_ext.rs | 48------------------------------------------------
Mcore/codegen_next/Cargo.toml | 6++----
Mcore/codegen_next/src/attribute/segments.rs | 32++++++++++++--------------------
Mcore/codegen_next/src/bang/uri.rs | 77+++++++++++++++++++++++++++++++++++------------------------------------------
Mcore/codegen_next/src/bang/uri_parsing.rs | 32+++++++++++++++++++-------------
Mcore/codegen_next/src/http_codegen.rs | 22+++++++++++-----------
Mcore/codegen_next/src/lib.rs | 385++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcore/codegen_next/tests/route.rs | 3+--
Mcore/codegen_next/tests/ui-fail/catch.rs | 4++--
Mcore/codegen_next/tests/ui-fail/catch.stderr | 8++++----
Mcore/http/src/uri/origin.rs | 2+-
Mcore/http/src/uri/segments.rs | 1-
18 files changed, 472 insertions(+), 659 deletions(-)

diff --git a/contrib/codegen/Cargo.toml b/contrib/codegen/Cargo.toml @@ -21,7 +21,7 @@ proc-macro = true [dependencies.derive_utils] git = "https://github.com/SergioBenitez/derive-utils" -rev = "87ad56ba" +rev = "f14fb4bc855" [dependencies] quote = "0.6" diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml @@ -11,12 +11,7 @@ keywords = ["rocket", "web", "framework", "code", "generation"] license = "MIT/Apache-2.0" build = "build.rs" -[lib] -plugin = true - [dependencies] -rocket_http = { version = "0.4.0-dev", path = "../http" } -indexmap = "1.0" [dev-dependencies] compiletest_rs = "0.3.14" diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs @@ -1,387 +0,0 @@ -#![crate_type = "dylib"] - -// TODO: Version URLs. -#![doc(html_root_url = "https://api.rocket.rs")] - -//! # 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). -//! -//! ## **Table of Contents** -//! -//! 1. [Custom Attributes](#custom-attributes) -//! 2. [Custom Derives](#custom-derives) -//! * [`FromForm`](#fromform) -//! * [`FromFormValue`](#fromformvalue) -//! * [`Responder`](#responder) -//! 3. [Procedural Macros](#procedural-macros) -//! 4. [Debugging Generated Code](#debugging-codegen) -//! -//! ## Custom Attributes -//! -//! This crate implements the following custom attributes: -//! -//! * **route** -//! * **get** -//! * **put** -//! * **post** -//! * **delete** -//! * **head** -//! * **patch** -//! * **options** -//! * **catch** -//! -//! 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 **catch** attribute is: -//! -//! <pre> -//! catch := INTEGER -//! </pre> -//! -//! A use of the `catch` attribute looks like: -//! -//! #[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: -//! -//! #[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 `field` name in the attribute is -//! used instead of the structure's actual field name when parsing a form. -//! -//! [`FromForm`]: /rocket/request/trait.FromForm.html -//! [`FromFormValue`]: /rocket/request/trait.FromFormValue.html -//! -//! ### `FromFormValue` -//! -//! The [`FromFormValue`] derive can be applied to enums with nullary -//! (zero-length) fields: -//! -//! #[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: -//! -//! #[derive(FromFormValue)] -//! enum MyValue { -//! First, -//! Second, -//! #[form(value = "fourth")] -//! Third, -//! } -//! -//! The attribute's grammar is: -//! -//! <pre> -//! form := 'field' '=' STRING_LIT -//! -//! STRING_LIT := any valid string literal, as defined by Rust -//! </pre> -//! -//! 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. -//! -//! #[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: -//! -//! #[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: -//! -//! <pre> -//! 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 -//! </pre> -//! -//! It can be used as follows: -//! -//! #[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/trait.Responder.html -//! [`Response`]: /rocket/struct.Response.html -//! [`Response::set_header()`]: /rocket/struct.Response.html#method.set_header -//! -//! ## Procedural Macros -//! -//! This crate implements the following procedural macros: -//! -//! * **routes** -//! * **catchers** -//! * **uri** -//! -//! The syntax for `routes!` and `catchers!` 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. 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: -//! -//! <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 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`: -//! -//! ``` -//! 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 -//! ``` diff --git a/core/codegen/src/utils/expr_ext.rs b/core/codegen/src/utils/expr_ext.rs @@ -1,18 +0,0 @@ -use syntax::ast::Expr; -use syntax::ast::ExprKind::*; - -pub trait ExprExt { - fn is_location(&self) -> bool; -} - -impl ExprExt for Expr { - fn is_location(&self) -> bool { - match self.node { - Path(..) => true, - Cast(ref expr, _) => expr.is_location(), - Field(ref expr, _) => expr.is_location(), - Index(ref expr, _) => expr.is_location(), - _ => false - } - } -} diff --git a/core/codegen/src/utils/ident_ext.rs b/core/codegen/src/utils/ident_ext.rs @@ -1,20 +0,0 @@ -use std::fmt::Display; -use syntax::ast::Ident; -use syntax::symbol::Symbol; - -pub trait IdentExt { - fn prepend<T: Display>(&self, other: T) -> Ident; - fn append<T: Display>(&self, other: T) -> Ident; -} - -impl IdentExt for Ident { - fn prepend<T: Display>(&self, other: T) -> Ident { - let new_ident = format!("{}{}", other, self.name); - Ident::new(Symbol::intern(&new_ident), self.span) - } - - fn append<T: Display>(&self, other: T) -> Ident { - let new_ident = format!("{}{}", self.name, other); - Ident::new(Symbol::intern(&new_ident), self.span) - } -} diff --git a/core/codegen/src/utils/parser_ext.rs b/core/codegen/src/utils/parser_ext.rs @@ -1,79 +0,0 @@ -use syntax::source_map; -use syntax::parse::{token, SeqSep, PResult}; -use syntax::parse::parser::{PathStyle, Parser}; -use syntax::parse::token::Token::{Eof, Comma}; -use syntax::ast::{self, Path, StrStyle, Ident}; -use syntax::symbol::Symbol; - -pub trait ParserExt<'a> { - // Parse a comma-seperated list of paths: `a::b, b::c`. - fn parse_paths(&mut self) -> PResult<'a, Vec<Path>>; - - // Just like `parse_str` but takes into account interpolated expressions. - fn parse_str_lit(&mut self) -> PResult<'a, (Symbol, StrStyle)>; - - // Like `parse_ident` but also looks for an `ident` in a `Pat`. - fn parse_ident_inc_pat(&mut self) -> PResult<'a, Ident>; - - // Duplicates previously removed method in libsyntax. - fn parse_seq<T, F>(&mut self, bra: &token::Token, ket: &token::Token, sep: SeqSep, f: F) - -> PResult<'a, source_map::Spanned<Vec<T>>> where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>; -} - -impl<'a> ParserExt<'a> for Parser<'a> { - fn parse_paths(&mut self) -> PResult<'a, Vec<Path>> { - self.parse_seq_to_end(&Eof, - SeqSep::trailing_allowed(Comma), - |p| p.parse_path(PathStyle::Mod)) - } - - fn parse_str_lit(&mut self) -> PResult<'a, (Symbol, StrStyle)> { - self.parse_str() - .or_else(|mut e| { - let expr = self.parse_expr().map_err(|i| { e.cancel(); i })?; - let string_lit = match expr.node { - ast::ExprKind::Lit(ref lit) => match lit.node { - ast::LitKind::Str(symbol, style) => (symbol, style), - _ => return Err(e) - } - _ => return Err(e) - }; - - e.cancel(); - Ok(string_lit) - }) - } - - fn parse_ident_inc_pat(&mut self) -> PResult<'a, Ident> { - self.parse_ident() - .or_else(|mut e| { - let pat = self.parse_pat().map_err(|i| { e.cancel(); i })?; - let ident = match pat.node { - ast::PatKind::Ident(_, ident, _) => ident, - _ => return Err(e) - }; - - e.cancel(); - Ok(ident) - }) - } - - // Duplicates previously removed method in libsyntax. NB: Do not use this - // function unless you actually plan to place the spanned list in the AST. - fn parse_seq<T, F>( - &mut self, - bra: &token::Token, - ket: &token::Token, - sep: SeqSep, - f: F - ) -> PResult<'a, source_map::Spanned<Vec<T>>> - where F: FnMut(&mut Parser<'a>) -> PResult<'a, T> - { - let lo = self.span; - self.expect(bra)?; - let result = self.parse_seq_to_before_end(ket, sep, f)?; - let hi = self.span; - self.bump(); - Ok(source_map::respan(lo.to(hi), result)) - } -} diff --git a/core/codegen/src/utils/span_ext.rs b/core/codegen/src/utils/span_ext.rs @@ -1,48 +0,0 @@ -use syntax::source_map::{Span, Spanned, BytePos}; - -pub trait SpanExt { - /// Trim the span on the left and right by `length`. - fn trim(self, length: u32) -> Span; - - /// Trim the span on the left by `length`. - fn trim_left(self, length: usize) -> Span; - - /// Trim the span on the right by `length`. - fn trim_right(self, length: usize) -> Span; - - // Trim from the right so that the span is `length` in size. - fn shorten_to(self, to_length: usize) -> Span; - - // Trim from the left so that the span is `length` in size. - fn shorten_upto(self, length: usize) -> Span; - - // Wrap `T` into a `Spanned<T>` with `self` as the span. - fn wrap<T>(self, node: T) -> Spanned<T>; -} - -impl SpanExt for Span { - fn trim_left(self, length: usize) -> Span { - self.with_lo(self.lo() + BytePos(length as u32)) - } - - fn trim_right(self, length: usize) -> Span { - self.with_hi(self.hi() - BytePos(length as u32)) - } - - fn shorten_to(self, to_length: usize) -> Span { - self.with_hi(self.lo() + BytePos(to_length as u32)) - } - - fn shorten_upto(self, length: usize) -> Span { - self.with_lo(self.hi() - BytePos(length as u32)) - } - - fn trim(self, length: u32) -> Span { - self.with_lo(self.lo() + BytePos(length)) - .with_hi(self.hi() - BytePos(length)) - } - - fn wrap<T>(self, node: T) -> Spanned<T> { - Spanned { node, span: self } - } -} diff --git a/core/codegen_next/Cargo.toml b/core/codegen_next/Cargo.toml @@ -20,12 +20,10 @@ proc-macro = true indexmap = "1.0" quote = "0.6.1" rocket_http = { version = "0.4.0-dev", path = "../http/" } -indexmap = "1" [dependencies.derive_utils] -path = "/Users/sbenitez/Sync/Data/Projects/Snippets/derive-utils/lib" -# git = "https://github.com/SergioBenitez/derive-utils" -# rev = "87ad56ba" +git = "https://github.com/SergioBenitez/derive-utils" +rev = "f14fb4bc855" [dev-dependencies] rocket = { version = "0.4.0-dev", path = "../lib" } diff --git a/core/codegen_next/src/attribute/segments.rs b/core/codegen_next/src/attribute/segments.rs @@ -6,15 +6,15 @@ use proc_macro::{Span, Diagnostic}; use http::route::RouteSegment; use proc_macro_ext::{SpanExt, Diagnostics, PResult, DResult}; -pub use http::route::{Error, Kind, Source}; +crate use http::route::{Error, Kind, Source}; #[derive(Debug, Clone)] -pub struct Segment { - pub span: Span, - pub kind: Kind, - pub source: Source, - pub name: String, - pub index: Option<usize>, +crate struct Segment { + crate span: Span, + crate kind: Kind, + crate source: Source, + crate name: String, + crate index: Option<usize>, } impl Segment { @@ -22,14 +22,6 @@ impl Segment { let (kind, source, index) = (segment.kind, segment.source, segment.index); Segment { span, kind, source, index, name: segment.name.into_owned() } } - - crate fn to_route_segment<'a>(&'a self) -> String { - match (self.source, self.kind) { - (_, Kind::Single) => format!("<{}>", self.name), - (_, Kind::Multi) => format!("<{}..>", self.name), - (_, Kind::Static) => self.name.clone() - } - } } impl<'a> From<&'a syn::Ident> for Segment { @@ -58,18 +50,18 @@ impl Hash for Segment { } } -pub fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { +fn subspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; let remaining = haystack.len() - (index + needle.len()); span.trimmed(index, remaining) } -pub fn trailspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { +fn trailspan(needle: &str, haystack: &str, span: Span) -> Option<Span> { let index = needle.as_ptr() as usize - haystack.as_ptr() as usize; span.trimmed(index - 1, 0) } -pub fn into_diagnostic( +fn into_diagnostic( segment: &str, // The segment that failed. source: &str, // The haystack where `segment` can be found. span: Span, // The `Span` of `Source`. @@ -111,13 +103,13 @@ pub fn into_diagnostic( } } -pub fn parse_segment(segment: &str, span: Span) -> PResult<Segment> { +crate fn parse_segment(segment: &str, span: Span) -> PResult<Segment> { RouteSegment::parse_one(segment) .map(|segment| Segment::from(segment, span)) .map_err(|e| into_diagnostic(segment, segment, span, &e)) } -pub fn parse_segments( +crate fn parse_segments( string: &str, sep: char, source: Source, diff --git a/core/codegen_next/src/bang/uri.rs b/core/codegen_next/src/bang/uri.rs @@ -1,17 +1,17 @@ +use std::fmt::Display; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use std::fmt::Display; use derive_utils::{syn, Result}; -use syn_ext::{IdentExt, syn_to_diag}; +use derive_utils::syn::{Expr, Ident, Type, spanned::Spanned}; +use http::{uri::Origin, ext::IntoOwned}; +use http::route::{RouteSegment, Kind, Source}; -use self::syn::{Expr, Ident, Type}; -use self::syn::spanned::Spanned as SynSpanned; +use http_codegen::Optional; +use syn_ext::{IdentExt, syn_to_diag}; use bang::{prefix_last_segment, uri_parsing::*}; -use rocket_http::{uri::Origin, ext::IntoOwned}; - -const URI_INFO_MACRO_PREFIX: &str = "rocket_uri_for_"; +use URI_MACRO_PREFIX; macro_rules! p { (@go $num:expr, $singular:expr, $plural:expr) => ( @@ -26,7 +26,7 @@ macro_rules! p { crate fn _uri_macro(input: TokenStream) -> Result<TokenStream> { let input2: TokenStream2 = input.clone().into(); let mut params = syn::parse::<UriParams>(input).map_err(syn_to_diag)?; - prefix_last_segment(&mut params.route_path, URI_INFO_MACRO_PREFIX); + prefix_last_segment(&mut params.route_path, URI_MACRO_PREFIX); let path = &params.route_path; Ok(quote!(#path!(#input2)).into()) @@ -80,21 +80,25 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> { } } -// Validates the mount path and the URI and returns a single Origin URI with -// both paths concatinated. Validation should always succeed since this macro -// can only be called if the route attribute succeed, which implies that the -// route URI was valid. -fn extract_origin(internal: &InternalUriParams) -> Result<Origin<'static>> { - let base_uri = match internal.uri_params.mount_point { - Some(ref base) => Origin::parse(&base.value()) - .map_err(|_| base.span().unstable().error("invalid path URI"))? - .into_owned(), - None => Origin::dummy() - }; - - Origin::parse_route(&format!("{}/{}", base_uri, internal.uri)) - .map(|o| o.to_normalized().into_owned()) - .map_err(|_| internal.uri.span().unstable().error("invalid route URI")) +// Returns an Origin URI with the mount point and route path concatinated. The +// query string is mangled by replacing single dynamic parameters in query parts +// (`<param>`) with `param=<param>`. +fn build_origin(internal: &InternalUriParams) -> Origin<'static> { + let mount_point = internal.uri_params.mount_point.as_ref() + .map(|origin| origin.path()) + .unwrap_or(""); + + let path = format!("{}/{}", mount_point, internal.route_uri.path()); + let query = RouteSegment::parse_query(&internal.route_uri).map(|segments| { + segments.map(|r| r.expect("invalid query segment")).map(|seg| { + match (seg.source, seg.kind) { + (Source::Query, Kind::Single) => format!("{k}=<{k}>", k = seg.name), + _ => seg.string.into_owned() + } + }).collect::<Vec<_>>().join("&") + }); + + Origin::new(path, query).to_normalized().into_owned() } fn explode<'a, I>(route_str: &str, items: I) -> TokenStream2 @@ -102,9 +106,7 @@ fn explode<'a, I>(route_str: &str, items: I) -> TokenStream2 { // Generate the statements to typecheck each parameter. // Building <$T as ::rocket::http::uri::FromUriParam<_>>::from_uri_param($e). - let mut let_bindings = vec![]; - let mut fmt_exprs = vec![]; - + let (mut let_bindings, mut fmt_exprs) = (vec![], vec![]); for (mut ident, ty, expr) in items { let (span, expr) = (expr.span(), expr); let ident_tmp = ident.prepend("tmp_"); @@ -143,31 +145,22 @@ crate fn _uri_internal_macro(input: TokenStream) -> Result<TokenStream> { // Parse the internal invocation and the user's URI param expressions. let internal = syn::parse::<InternalUriParams>(input).map_err(syn_to_diag)?; let exprs = extract_exprs(&internal)?; - let origin = extract_origin(&internal)?; - - // Determine how many parameters there are in the URI path. - let path_param_count = origin.path().matches('<').count(); // Create an iterator over the `ident`, `ty`, and `expr` triple. let mut arguments = internal.fn_args.iter() .zip(exprs.iter()) .map(|(FnArg { ident, ty }, &expr)| (ident, ty, expr)); - // Generate an expression for both the path and query. + // Generate an expression for the path and query. + let origin = build_origin(&internal); + let path_param_count = origin.path().matches('<').count(); let path = explode(origin.path(), arguments.by_ref().take(path_param_count)); - - // FIXME: Use Optional. - // let query = Optional(origin.query().map(|q| explode(q, arguments))); - let query = if let Some(expr) = origin.query().map(|q| explode(q, arguments)) { - quote!({ Some(#expr) }) - } else { - quote!({ None }) - }; + let query = Optional(origin.query().map(|q| explode(q, arguments))); Ok(quote!({ ::rocket::http::uri::Origin::new::< - ::std::borrow::Cow<'static, str>, - ::std::borrow::Cow<'static, str>, - >(#path, #query) + ::std::borrow::Cow<'static, str>, + ::std::borrow::Cow<'static, str>, + >(#path, #query) }).into()) } diff --git a/core/codegen_next/src/bang/uri_parsing.rs b/core/codegen_next/src/bang/uri_parsing.rs @@ -9,6 +9,7 @@ use self::syn::{Expr, Ident, LitStr, Path, Token, Type}; use self::syn::parse::{self, Parse, ParseStream}; use self::syn::punctuated::Punctuated; +use http::{uri::Origin, ext::IntoOwned}; use indexmap::IndexMap; #[derive(Debug)] @@ -30,7 +31,7 @@ pub enum Args { // uri_params.route_path #[derive(Debug)] pub struct UriParams { - pub mount_point: Option<LitStr>, + pub mount_point: Option<Origin<'static>>, pub route_path: Path, pub arguments: Args, } @@ -62,11 +63,11 @@ pub enum Validation<'a> { // `uri` is the full URI used in the origin route's attribute. // // internal_uri!("/<first>/<second>", (first: ty, second: ty), $($tt)*); -// ^-----------------| ^-----------|---------| ^-----| -// uri fn_args uri_params +// ^--------|--------- ^-----------|---------| ^-----| +// route_uri fn_args uri_params #[derive(Debug)] pub struct InternalUriParams { - pub uri: String, + pub route_uri: Origin<'static>, pub fn_args: Vec<FnArg>, pub uri_params: UriParams, } @@ -100,12 +101,11 @@ impl Parse for UriParams { // Parse the mount point and suffixing ',', if any. let mount_point = if input.peek(LitStr) { let string = input.parse::<LitStr>()?; - let value = string.value(); - if value.contains('<') || !value.starts_with('/') { - // TODO(proc_macro): add example as a help, not in error - return err(string.span().unstable(), "invalid mount point; \ - mount points must be static, absolute URIs: `/example`"); - } + let mount_point = Origin::parse_owned(string.value()).map_err(|_| { + // TODO(proc_macro): use error, add example as a help + parse::Error::new(string.span(), "invalid mount point; \ + mount points must be static, absolute URIs: `/example`") + })?; if !input.peek(Token![,]) && input.cursor().eof() { return err(string.span().unstable(), "unexpected end of input: \ @@ -113,7 +113,7 @@ impl Parse for UriParams { } input.parse::<Token![,]>()?; - Some(string) + Some(mount_point) } else { None }; @@ -171,9 +171,15 @@ impl Parse for FnArg { impl Parse for InternalUriParams { fn parse(input: ParseStream) -> parse::Result<InternalUriParams> { - let uri = input.parse::<LitStr>()?.value(); + let route_uri_str = input.parse::<LitStr>()?; input.parse::<Token![,]>()?; + // Validation should always succeed since this macro can only be called + // if the route attribute succeeded, implying a valid route URI. + let route_uri = Origin::parse_route(&route_uri_str.value()) + .map(|o| o.to_normalized().into_owned()) + .map_err(|_| input.error("internal error: invalid route URI"))?; + let content; syn::parenthesized!(content in input); let fn_args: Punctuated<FnArg, Token![,]> = content.parse_terminated(FnArg::parse)?; @@ -181,7 +187,7 @@ impl Parse for InternalUriParams { input.parse::<Token![,]>()?; let uri_params = input.parse::<UriParams>()?; - Ok(InternalUriParams { uri, fn_args, uri_params }) + Ok(InternalUriParams { route_uri, fn_args, uri_params }) } } diff --git a/core/codegen_next/src/http_codegen.rs b/core/codegen_next/src/http_codegen.rs @@ -7,31 +7,31 @@ use attribute::segments::{parse_segments, parse_segment, Segment, Kind, Source}; use proc_macro_ext::SpanExt; #[derive(Debug)] -pub struct ContentType(pub http::ContentType); +crate struct ContentType(crate http::ContentType); #[derive(Debug)] -pub struct Status(pub http::Status); +crate struct Status(crate http::Status); #[derive(Debug)] -pub struct MediaType(pub http::MediaType); +crate struct MediaType(crate http::MediaType); #[derive(Debug)] -pub struct Method(pub http::Method); +crate struct Method(crate http::Method); #[derive(Debug)] -pub struct Origin(pub http::uri::Origin<'static>); +crate struct Origin(crate http::uri::Origin<'static>); #[derive(Clone, Debug)] -pub struct DataSegment(pub Segment); +crate struct DataSegment(crate Segment); #[derive(Clone, Debug)] -pub struct Optional<T>(pub Option<T>); +crate struct Optional<T>(crate Option<T>); #[derive(Debug)] -pub struct RoutePath { - pub origin: Origin, - pub path: Vec<Segment>, - pub query: Option<Vec<Segment>>, +crate struct RoutePath { + crate origin: Origin, + crate path: Vec<Segment>, + crate query: Option<Vec<Segment>>, } impl FromMeta for Status { diff --git a/core/codegen_next/src/lib.rs b/core/codegen_next/src/lib.rs @@ -4,9 +4,391 @@ #![feature(rustc_private)] #![recursion_limit="128"] +//! # 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). +//! +//! ## **Table of Contents** +//! +//! 1. [Custom Attributes](#custom-attributes) +//! 2. [Custom Derives](#custom-derives) +//! * [`FromForm`](#fromform) +//! * [`FromFormValue`](#fromformvalue) +//! * [`Responder`](#responder) +//! 3. [Procedural Macros](#procedural-macros) +//! 4. [Debugging Generated Code](#debugging-codegen) +//! +//! ## Custom Attributes +//! +//! This crate implements the following custom attributes: +//! +//! * **route** +//! * **get** +//! * **put** +//! * **post** +//! * **delete** +//! * **head** +//! * **patch** +//! * **options** +//! * **catch** +//! +//! 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 **catch** attribute is: +//! +//! <pre> +//! catch := INTEGER +//! </pre> +//! +//! A use of the `catch` attribute looks like: +//! +//! #[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: +//! +//! #[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 `field` name in the attribute is +//! used instead of the structure's actual field name when parsing a form. +//! +//! [`FromForm`]: /rocket/request/trait.FromForm.html +//! [`FromFormValue`]: /rocket/request/trait.FromFormValue.html +//! +//! ### `FromFormValue` +//! +//! The [`FromFormValue`] derive can be applied to enums with nullary +//! (zero-length) fields: +//! +//! #[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: +//! +//! #[derive(FromFormValue)] +//! enum MyValue { +//! First, +//! Second, +//! #[form(value = "fourth")] +//! Third, +//! } +//! +//! The attribute's grammar is: +//! +//! <pre> +//! form := 'field' '=' STRING_LIT +//! +//! STRING_LIT := any valid string literal, as defined by Rust +//! </pre> +//! +//! 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. +//! +//! #[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: +//! +//! #[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: +//! +//! <pre> +//! 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 +//! </pre> +//! +//! It can be used as follows: +//! +//! #[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/trait.Responder.html +//! [`Response`]: /rocket/struct.Response.html +//! [`Response::set_header()`]: /rocket/struct.Response.html#method.set_header +//! +//! ## Procedural Macros +//! +//! This crate implements the following procedural macros: +//! +//! * **routes** +//! * **catchers** +//! * **uri** +//! +//! The syntax for `routes!` and `catchers!` 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. 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: +//! +//! <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 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`: +//! +//! ``` +//! 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 +//! ``` + #[macro_use] extern crate quote; #[macro_use] extern crate derive_utils; -extern crate indexmap; extern crate proc_macro; extern crate rocket_http as http; extern crate indexmap; @@ -39,6 +421,7 @@ macro_rules! route_attribute { } ) } + route_attribute!(route => None); route_attribute!(get => Method::Get); route_attribute!(put => Method::Put); diff --git a/core/codegen_next/tests/route.rs b/core/codegen_next/tests/route.rs @@ -1,5 +1,4 @@ -#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)] -#![plugin(rocket_codegen)] +#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)] #[macro_use] extern crate rocket; diff --git a/core/codegen_next/tests/ui-fail/catch.rs b/core/codegen_next/tests/ui-fail/catch.rs @@ -16,11 +16,11 @@ const CATCH: &str = "Catcher"; //~^ HELP #[catch(404)] fn e1(_request: &Request) { } -#[catch(code = "404")] //~ ERROR unexpected parameter +#[catch(code = "404")] //~ ERROR unexpected keyed parameter //~^ HELP #[catch(404)] fn e2(_request: &Request) { } -#[catch(code = 404)] //~ ERROR unexpected parameter +#[catch(code = 404)] //~ ERROR unexpected keyed parameter //~^ HELP #[catch(404)] fn e3(_request: &Request) { } diff --git a/core/codegen_next/tests/ui-fail/catch.stderr b/core/codegen_next/tests/ui-fail/catch.stderr @@ -22,18 +22,18 @@ error: invalid value: expected unsigned integer literal | = help: `#[catch]` expects a single status integer, e.g.: #[catch(404)] -error: unexpected parameter: expected literal or identifier +error: unexpected keyed parameter: expected literal or identifier --> $DIR/catch.rs:19:9 | -19 | #[catch(code = "404")] //~ ERROR unexpected named parameter +19 | #[catch(code = "404")] //~ ERROR unexpected keyed parameter | ^^^^^^^^^^^^ | = help: `#[catch]` expects a single status integer, e.g.: #[catch(404)] -error: unexpected parameter: expected literal or identifier +error: unexpected keyed parameter: expected literal or identifier --> $DIR/catch.rs:23:9 | -23 | #[catch(code = 404)] //~ ERROR unexpected named parameter +23 | #[catch(code = 404)] //~ ERROR unexpected keyed parameter | ^^^^^^^^^^ | = help: `#[catch]` expects a single status integer, e.g.: #[catch(404)] diff --git a/core/http/src/uri/origin.rs b/core/http/src/uri/origin.rs @@ -127,7 +127,7 @@ impl<'a> Origin<'a> { // Used mostly for testing and to construct known good URIs from other parts // of Rocket. This should _really_ not be used outside of Rocket because the - // resulting `Origin's` may not be valid origin URIs! + // resulting `Origin's` are not guaranteed to be valid origin URIs! #[doc(hidden)] pub fn new<P, Q>(path: P, query: Option<Q>) -> Origin<'a> where P: Into<Cow<'a, str>>, Q: Into<Cow<'a, str>> diff --git a/core/http/src/uri/segments.rs b/core/http/src/uri/segments.rs @@ -120,4 +120,3 @@ impl<'a> Iterator for Segments<'a> { // self.1.unwrap_or_else(self.fold(0, |cnt, _| cnt + 1)) // } } -