mirror of https://github.com/rwf2/Rocket.git
Port all codegen tests to codegen_next.
This commit is contained in:
parent
b9bf1ee37d
commit
360b0e80b0
|
@ -4,7 +4,6 @@ codegen-units = 4
|
|||
[workspace]
|
||||
members = [
|
||||
"core/lib/",
|
||||
"core/codegen/",
|
||||
"core/codegen_next/",
|
||||
"core/http/",
|
||||
"contrib/lib",
|
||||
|
|
|
@ -21,7 +21,7 @@ proc-macro = true
|
|||
|
||||
[dependencies.derive_utils]
|
||||
git = "https://github.com/SergioBenitez/derive-utils"
|
||||
rev = "f14fb4bc855"
|
||||
rev = "62f361f"
|
||||
|
||||
[dependencies]
|
||||
quote = "0.6"
|
||||
|
|
|
@ -68,9 +68,6 @@ r2d2_redis = { version = "0.8", optional = true }
|
|||
# Contrib codegen dependencies
|
||||
rocket_contrib_codegen = { path = "../codegen", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" }
|
||||
|
||||
[target.'cfg(debug_assertions)'.dependencies]
|
||||
notify = { version = "^4.0" }
|
||||
|
||||
|
|
|
@ -74,10 +74,9 @@
|
|||
//! Whenever a connection to the database is needed:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(plugin, decl_macro)]
|
||||
//! # #![plugin(rocket_codegen)]
|
||||
//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
//! #
|
||||
//! # extern crate rocket;
|
||||
//! # #[macro_use] extern crate rocket;
|
||||
//! # extern crate rocket_contrib;
|
||||
//! #
|
||||
//! # use rocket_contrib::databases::{database, diesel};
|
||||
|
@ -263,10 +262,9 @@
|
|||
//! connection to a given database:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(plugin, decl_macro)]
|
||||
//! # #![plugin(rocket_codegen)]
|
||||
//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
//! #
|
||||
//! # extern crate rocket;
|
||||
//! # #[macro_use] extern crate rocket;
|
||||
//! # extern crate rocket_contrib;
|
||||
//! # use rocket_contrib::databases::{database, diesel};
|
||||
//! #[database("my_db")]
|
||||
|
@ -285,10 +283,9 @@
|
|||
//! connection type:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #![feature(plugin, decl_macro)]
|
||||
//! # #![plugin(rocket_codegen)]
|
||||
//! # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
//! #
|
||||
//! # extern crate rocket;
|
||||
//! # #[macro_use] extern crate rocket;
|
||||
//! # extern crate rocket_contrib;
|
||||
//! # use rocket_contrib::databases::{database, diesel};
|
||||
//! #
|
||||
|
|
|
@ -27,9 +27,8 @@ pub use self::rmp_serde::decode::Error as MsgPackError;
|
|||
/// HTTP request body.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin, decl_macro)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// # extern crate rocket_contrib;
|
||||
/// # type User = usize;
|
||||
/// # fn main() { }
|
||||
|
@ -55,9 +54,8 @@ pub use self::rmp_serde::decode::Error as MsgPackError;
|
|||
/// is set to `application/msgpack` automatically.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin, decl_macro)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// # extern crate rocket_contrib;
|
||||
/// # type User = usize;
|
||||
/// # fn main() { }
|
||||
|
|
|
@ -30,9 +30,8 @@ use super::ContextManager;
|
|||
/// can be used as a request guard in any request handler.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(plugin, decl_macro)]
|
||||
/// # #![plugin(rocket_codegen)]
|
||||
/// # extern crate rocket;
|
||||
/// # #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// # #[macro_use] extern crate rocket_contrib;
|
||||
/// # fn main() { }
|
||||
/// #
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
[package]
|
||||
name = "rocket_codegen"
|
||||
version = "0.4.0-dev"
|
||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||
description = "Code generation for the Rocket web framework."
|
||||
documentation = "https://api.rocket.rs/rocket_codegen/"
|
||||
homepage = "https://rocket.rs"
|
||||
repository = "https://github.com/SergioBenitez/Rocket"
|
||||
readme = "../../README.md"
|
||||
keywords = ["rocket", "web", "framework", "code", "generation"]
|
||||
license = "MIT/Apache-2.0"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = "0.3.14"
|
||||
rocket = { version = "0.4.0-dev", path = "../lib" }
|
||||
|
||||
[build-dependencies]
|
||||
yansi = "0.4"
|
||||
version_check = "0.1.3"
|
|
@ -1,58 +0,0 @@
|
|||
//! This tiny build script ensures that rocket_codegen is not compiled with an
|
||||
//! incompatible version of rust.
|
||||
|
||||
extern crate yansi;
|
||||
extern crate version_check;
|
||||
|
||||
use yansi::Color::{Red, Yellow, Blue, White};
|
||||
use version_check::{supports_features, is_min_version, is_min_date};
|
||||
|
||||
// Specifies the minimum nightly version needed to compile Rocket's codegen.
|
||||
const MIN_DATE: &'static str = "2018-08-23";
|
||||
const MIN_VERSION: &'static str = "1.30.0-nightly";
|
||||
|
||||
fn main() {
|
||||
let ok_channel = supports_features();
|
||||
let ok_version = is_min_version(MIN_VERSION);
|
||||
let ok_date = is_min_date(MIN_DATE);
|
||||
|
||||
let print_version_err = |version: &str, date: &str| {
|
||||
eprintln!("{} {}. {} {}.",
|
||||
White.paint("Installed version is:"),
|
||||
Yellow.paint(format!("{} ({})", version, date)),
|
||||
White.paint("Minimum required:"),
|
||||
Yellow.paint(format!("{} ({})", MIN_VERSION, MIN_DATE)));
|
||||
};
|
||||
|
||||
match (ok_channel, ok_version, ok_date) {
|
||||
(Some(ok_channel), Some((ok_version, version)), Some((ok_date, date))) => {
|
||||
if !ok_channel {
|
||||
eprintln!("{} {}",
|
||||
Red.paint("Error:").bold(),
|
||||
White.paint("Rocket requires a nightly or dev version of Rust."));
|
||||
print_version_err(&*version, &*date);
|
||||
eprintln!("{}{}{}",
|
||||
Blue.paint("See the getting started guide ("),
|
||||
White.paint("https://rocket.rs/guide/getting-started/"),
|
||||
Blue.paint(") for more information."));
|
||||
panic!("Aborting compilation due to incompatible compiler.")
|
||||
}
|
||||
|
||||
if !ok_version || !ok_date {
|
||||
eprintln!("{} {}",
|
||||
Red.paint("Error:").bold(),
|
||||
White.paint("Rocket codegen requires a more recent version of rustc."));
|
||||
eprintln!("{}{}{}",
|
||||
Blue.paint("Use `"),
|
||||
White.paint("rustup update"),
|
||||
Blue.paint("` or your preferred method to update Rust."));
|
||||
print_version_err(&*version, &*date);
|
||||
panic!("Aborting compilation due to incompatible compiler.")
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
println!("cargo:warning={}", "Rocket was unable to check rustc compatibility.");
|
||||
println!("cargo:warning={}", "Build may fail due to incompatible rustc version.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
#[get("a")] //~ ERROR invalid
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
#[get("")] //~ ERROR invalid
|
||||
fn get1(id: usize) -> &'static str { "hi" }
|
||||
|
||||
#[get("a/b/c")] //~ ERROR invalid
|
||||
fn get2(id: usize) -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
|
@ -1,13 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get] //~ ERROR incorrect use of attribute
|
||||
//~^ ERROR malformed attribute
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get(path = "/hello", 123)] //~ ERROR expected
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/")]
|
||||
fn get(_: usize) -> &'static str { "hi" } //~ ERROR argument
|
||||
|
||||
fn main() { }
|
|
@ -1,20 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get(1)] //~ ERROR expected `path = string`
|
||||
fn get0() -> &'static str { "hi" }
|
||||
|
||||
#[get(path = 1)] //~ ERROR must be a string
|
||||
fn get1() -> &'static str { "hi" }
|
||||
|
||||
#[get(path = "/", rank = "2")] //~ ERROR must be an int
|
||||
fn get2() -> &'static str { "hi" }
|
||||
|
||||
#[get(path = "/", format = 100)] //~ ERROR must be a "media/type"
|
||||
fn get3() -> &'static str { "hi" }
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("")] //~ ERROR can only be used on functions
|
||||
enum B { } //~ ERROR but was applied
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("")] //~ ERROR can only be used on functions
|
||||
impl C for A { } //~ ERROR but was applied
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("")] //~ ERROR can only be used on functions
|
||||
struct A; //~ ERROR but was applied
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("")] //~ ERROR can only be used on functions
|
||||
trait C { } //~ ERROR but was applied
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
#[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
#[get("//")] //~ ERROR paths cannot contain empty segments
|
||||
fn get1(name: &str) -> &'static str { "hi" }
|
||||
|
||||
#[get("/a/")] //~ ERROR paths cannot contain empty segments
|
||||
fn get2(name: &str) -> &'static str { "hi" }
|
||||
|
||||
#[get("////")] //~ ERROR paths cannot contain empty segments
|
||||
fn get3() -> &'static str { "hi" }
|
||||
|
||||
#[get("/a///")] //~ ERROR paths cannot contain empty segments
|
||||
fn get4() -> &'static str { "hi" }
|
||||
|
||||
#[get("/a/b//")] //~ ERROR paths cannot contain empty segments
|
||||
fn get5() -> &'static str { "hi" }
|
||||
|
||||
#[get("/a/b/c/")] //~ ERROR paths cannot contain empty segments
|
||||
fn get6() -> &'static str { "hi" }
|
||||
|
||||
#[get("/a/b/c/d//e/")] //~ ERROR paths cannot contain empty segments
|
||||
fn get7() -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
|
@ -1,16 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
#[get("/<name>")] //~ ERROR unused dynamic parameter: `name`
|
||||
fn get(other: usize) -> &'static str { "hi" } //~ NOTE expected
|
||||
|
||||
#[get("/a?<r>")] //~ ERROR unused dynamic parameter: `r`
|
||||
fn get1() -> &'static str { "hi" } //~ NOTE expected
|
||||
|
||||
#[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter: `test`
|
||||
fn post() -> &'static str { "hi" } //~ NOTE expected
|
||||
|
||||
#[get("/<_r>")] //~ ERROR unused dynamic parameter: `_r`
|
||||
fn get2(r: usize) -> &'static str { "hi" } //~ NOTE expected
|
||||
|
||||
fn main() { }
|
|
@ -1,38 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/", format = "applicationx-custom")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn one() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn two() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "//")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn three() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn four() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "a/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn five() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/a")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn six() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "/a/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn seven() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "a/b/")] //~ ERROR malformed
|
||||
//~^ ERROR `format` must be a "media/type"
|
||||
fn eight() -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
|
@ -1,17 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
#[get("/<param>")] //~ ERROR unused dynamic parameter: `param`
|
||||
fn get() { } //~ NOTE expected
|
||||
|
||||
#[get("/<a>")] //~ ERROR unused dynamic parameter: `a`
|
||||
fn get2() { } //~ NOTE expected
|
||||
|
||||
#[get("/a/b/c/<a>/<b>")]
|
||||
//~^ ERROR unused dynamic parameter: `a`
|
||||
//~^^ ERROR unused dynamic parameter: `b`
|
||||
fn get32() { }
|
||||
//~^ NOTE expected
|
||||
//~^^ NOTE expected
|
||||
|
||||
fn main() { }
|
|
@ -1,12 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get(path = "/hello", unknown = 123)] //~ ERROR 'unknown' is not a known param
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
fn main() {
|
||||
let _ = routes![get];
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// must-compile-successfully
|
||||
|
||||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/", format = "application/x-custom")] //~ WARNING not a known media type
|
||||
fn one() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type
|
||||
fn two() -> &'static str { "hi" }
|
||||
|
||||
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
||||
fn three() -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
|
@ -1,104 +0,0 @@
|
|||
extern crate compiletest_rs as compiletest;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{io, fs::Metadata, time::SystemTime};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Kind {
|
||||
Dynamic, Static
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
fn extension(self) -> &'static str {
|
||||
match self {
|
||||
#[cfg(windows)] Kind::Dynamic => ".dll",
|
||||
#[cfg(all(unix, target_os = "macos"))] Kind::Dynamic => ".dylib",
|
||||
#[cfg(all(unix, not(target_os = "macos")))] Kind::Dynamic => ".so",
|
||||
Kind::Static => ".rlib"
|
||||
}
|
||||
}
|
||||
|
||||
fn prefix(self) -> &'static str {
|
||||
#[cfg(windows)] { "" }
|
||||
#[cfg(not(windows))] { "lib" }
|
||||
}
|
||||
}
|
||||
|
||||
fn target_path() -> PathBuf {
|
||||
#[cfg(debug_assertions)] const ENVIRONMENT: &str = "debug";
|
||||
#[cfg(not(debug_assertions))] const ENVIRONMENT: &str = "release";
|
||||
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.parent().unwrap().parent().unwrap()
|
||||
.join("target")
|
||||
.join(ENVIRONMENT)
|
||||
}
|
||||
|
||||
fn link_flag(flag: &str, lib: &str, rel_path: &[&str]) -> String {
|
||||
let mut path = target_path();
|
||||
for component in rel_path {
|
||||
path = path.join(component);
|
||||
}
|
||||
|
||||
format!("{} {}={}", flag, lib, path.display())
|
||||
}
|
||||
|
||||
fn best_time_for(metadata: &Metadata) -> SystemTime {
|
||||
metadata.created()
|
||||
.or_else(|_| metadata.modified())
|
||||
.or_else(|_| metadata.accessed())
|
||||
.unwrap_or_else(|_| SystemTime::now())
|
||||
}
|
||||
|
||||
fn extern_dep(name: &str, kind: Kind) -> io::Result<String> {
|
||||
let deps_root = target_path().join("deps");
|
||||
let dep_name = format!("{}{}", kind.prefix(), name);
|
||||
|
||||
let mut dep_path: Option<PathBuf> = None;
|
||||
for entry in deps_root.read_dir().expect("read_dir call failed") {
|
||||
let entry = match entry {
|
||||
Ok(entry) => entry,
|
||||
Err(_) => continue
|
||||
};
|
||||
|
||||
let filename = entry.file_name();
|
||||
let filename = filename.to_string_lossy();
|
||||
let lib_name = filename.split('.').next().unwrap().split('-').next().unwrap();
|
||||
|
||||
if lib_name == dep_name && filename.ends_with(kind.extension()) {
|
||||
if let Some(ref mut existing) = dep_path {
|
||||
if best_time_for(&entry.metadata()?) > best_time_for(&existing.metadata()?) {
|
||||
*existing = entry.path().into();
|
||||
}
|
||||
} else {
|
||||
dep_path = Some(entry.path().into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dep = dep_path.ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?;
|
||||
let filename = dep.file_name().ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
Ok(link_flag("--extern", name, &["deps", &filename.to_string_lossy()]))
|
||||
}
|
||||
|
||||
fn run_mode(mode: &'static str) {
|
||||
let mut config = compiletest::Config::default();
|
||||
config.mode = mode.parse().expect("Invalid mode");
|
||||
config.src_base = format!("tests/{}", mode).into();
|
||||
config.clean_rmeta();
|
||||
|
||||
config.target_rustcflags = Some([
|
||||
link_flag("-L", "crate", &[]),
|
||||
link_flag("-L", "dependency", &["deps"]),
|
||||
extern_dep("rocket_codegen", Kind::Dynamic).expect("find codegen dep"),
|
||||
extern_dep("rocket", Kind::Static).expect("find core dep")
|
||||
].join(" "));
|
||||
|
||||
compiletest::run_tests(&config);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_test() {
|
||||
run_mode("compile-fail");
|
||||
run_mode("ui");
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
error: error catchers can have at most 2 arguments
|
||||
--> $DIR/bad-error-fn.rs:9:1
|
||||
|
|
||||
9 | fn err_a(_a: Error, _b: Request, _c: Error) -> &'static str { "hi" }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: error catchers can take either a `rocket::Error`, `rocket::Request` type, or both.
|
||||
|
||||
error: unknown error catcher argument
|
||||
--> $DIR/bad-error-fn.rs:12:14
|
||||
|
|
||||
12 | fn err_b(_a: (isize, usize)) -> &'static str { "hi" }
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: error catchers can take either a `rocket::Error`, `rocket::Request` type, or both.
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[get("/", data = "<something>")]
|
||||
fn get(something: rocket::Data) -> &'static str { "hi" }
|
||||
|
||||
fn main() { }
|
|
@ -1,9 +0,0 @@
|
|||
warning: `data` route parameter used with non-payload-supporting method
|
||||
--> $DIR/data-without-post.rs:6:12
|
||||
|
|
||||
6 | #[get("/", data = "<something>")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: 'GET' does not typically support payloads
|
||||
= note: the 'format' attribute parameter will match against the 'Accept' header
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
#[get("/><")]
|
||||
fn get() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<id><")]
|
||||
fn get1(id: usize) -> &'static str { "hi" }
|
||||
|
||||
#[get("/<<<<id><")]
|
||||
fn get2(id: usize) -> &'static str { "hi" }
|
||||
|
||||
#[get("/<!>")]
|
||||
fn get3() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<_>")]
|
||||
fn get4() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<1>")]
|
||||
fn get5() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<>name><")]
|
||||
fn get6() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<name>:<id>")]
|
||||
fn get7() -> &'static str { "hi" }
|
||||
|
||||
#[get("/<>")]
|
||||
fn get8() -> &'static str { "hi" }
|
|
@ -1,72 +0,0 @@
|
|||
error: malformed parameter
|
||||
--> $DIR/malformed-param-list.rs:4:9
|
||||
|
|
||||
4 | #[get("/><")]
|
||||
| ^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
|
||||
error: malformed parameter
|
||||
--> $DIR/malformed-param-list.rs:7:9
|
||||
|
|
||||
7 | #[get("/<id><")]
|
||||
| ^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
|
||||
error: malformed parameter
|
||||
--> $DIR/malformed-param-list.rs:10:9
|
||||
|
|
||||
10 | #[get("/<<<<id><")]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
|
||||
error: parameter names must be valid identifiers
|
||||
--> $DIR/malformed-param-list.rs:13:9
|
||||
|
|
||||
13 | #[get("/<!>")]
|
||||
| ^^^
|
||||
|
|
||||
= note: "!" is not a valid identifier
|
||||
|
||||
error: parameters must be named
|
||||
--> $DIR/malformed-param-list.rs:16:9
|
||||
|
|
||||
16 | #[get("/<_>")]
|
||||
| ^^^
|
||||
|
|
||||
= help: use a name such as `_guard` or `_param`
|
||||
|
||||
error: parameter names must be valid identifiers
|
||||
--> $DIR/malformed-param-list.rs:19:9
|
||||
|
|
||||
19 | #[get("/<1>")]
|
||||
| ^^^
|
||||
|
|
||||
= note: "1" is not a valid identifier
|
||||
|
||||
error: malformed parameter
|
||||
--> $DIR/malformed-param-list.rs:22:9
|
||||
|
|
||||
22 | #[get("/<>name><")]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
|
||||
error: parameter names must be valid identifiers
|
||||
--> $DIR/malformed-param-list.rs:25:9
|
||||
|
|
||||
25 | #[get("/<name>:<id>")]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: "name>:<id" is not a valid identifier
|
||||
|
||||
error: parameters cannot be empty
|
||||
--> $DIR/malformed-param-list.rs:28:9
|
||||
|
|
||||
28 | #[get("/<>")]
|
||||
| ^^
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
#[route(FIX, "/hello")]
|
||||
fn get1() -> &'static str { "hi" }
|
||||
|
||||
#[route("hi", "/hello")]
|
||||
fn get2() -> &'static str { "hi" }
|
||||
|
||||
#[route("GET", "/hello")]
|
||||
fn get3() -> &'static str { "hi" }
|
||||
|
||||
#[route(120, "/hello")]
|
||||
fn get4() -> &'static str { "hi" }
|
||||
|
||||
#[route(CONNECT, "/hello")]
|
||||
fn get5() -> &'static str { "hi" }
|
|
@ -1,42 +0,0 @@
|
|||
error: 'FIX' is not a valid method
|
||||
--> $DIR/route-bad-method.rs:6:9
|
||||
|
|
||||
6 | #[route(FIX, "/hello")]
|
||||
| ^^^
|
||||
|
|
||||
= help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected a valid HTTP method identifier
|
||||
--> $DIR/route-bad-method.rs:9:9
|
||||
|
|
||||
9 | #[route("hi", "/hello")]
|
||||
| ^^^^
|
||||
|
|
||||
= help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected a valid HTTP method identifier
|
||||
--> $DIR/route-bad-method.rs:12:9
|
||||
|
|
||||
12 | #[route("GET", "/hello")]
|
||||
| ^^^^^
|
||||
|
|
||||
= help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected a valid HTTP method identifier
|
||||
--> $DIR/route-bad-method.rs:15:9
|
||||
|
|
||||
15 | #[route(120, "/hello")]
|
||||
| ^^^
|
||||
|
|
||||
= help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: 'CONNECT' is not a valid method
|
||||
--> $DIR/route-bad-method.rs:18:9
|
||||
|
|
||||
18 | #[route(CONNECT, "/hello")]
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: valid methods are: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::http::RawStr;
|
||||
use rocket::request::FromParam;
|
||||
|
||||
struct S;
|
||||
|
||||
impl<'a> FromParam<'a> for S {
|
||||
type Error = ();
|
||||
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> { Ok(S) }
|
||||
}
|
||||
|
||||
#[post("/<id>")]
|
||||
fn simple(id: i32) -> &'static str { "" }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn not_uri_display(id: i32, name: S) -> &'static str { "" }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn not_uri_display_but_unused(id: i32, name: S) -> &'static str { "" }
|
||||
|
||||
fn main() {
|
||||
uri!(simple: id = "hi");
|
||||
uri!(simple: "hello");
|
||||
uri!(simple: id = 239239i64);
|
||||
uri!(not_uri_display: 10, S);
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use rocket::http::Cookies;
|
||||
|
||||
#[post("/<id>")]
|
||||
fn simple(id: i32) -> &'static str { "" }
|
||||
|
||||
#[post("/<id>")]
|
||||
fn guard_1(cookies: Cookies, id: i32) -> &'static str { "" }
|
||||
|
||||
fn main() {
|
||||
uri!(simple);
|
||||
uri!(simple: 1, 23);
|
||||
uri!(simple: "Hello", 23, );
|
||||
uri!(guard_1: "hi", 100);
|
||||
|
||||
uri!(simple: id = 100, name = "hi");
|
||||
uri!(simple: id = 100, id = 100);
|
||||
uri!(simple: name = 100, id = 100);
|
||||
uri!(simple: id = 100, id = 100, );
|
||||
uri!(simple: name = "hi");
|
||||
uri!(guard_1: cookies = "hi", id = 100);
|
||||
uri!(guard_1: id = 100, cookies = "hi");
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
error: `simple` route uri expects 1 parameter but 0 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:18:10
|
||||
|
|
||||
18 | uri!(simple);
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `simple` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:19:18
|
||||
|
|
||||
19 | uri!(simple: 1, 23);
|
||||
| ^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `simple` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:20:18
|
||||
|
|
||||
20 | uri!(simple: "Hello", 23, );
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `guard_1` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:21:19
|
||||
|
|
||||
21 | uri!(guard_1: "hi", 100);
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: invalid parameters for `simple` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:23:18
|
||||
|
|
||||
23 | uri!(simple: id = 100, name = "hi");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:23:28
|
||||
|
|
||||
23 | uri!(simple: id = 100, name = "hi");
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `simple` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:24:18
|
||||
|
|
||||
24 | uri!(simple: id = 100, id = 100);
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:24:28
|
||||
|
|
||||
24 | uri!(simple: id = 100, id = 100);
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `simple` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:25:18
|
||||
|
|
||||
25 | uri!(simple: name = 100, id = 100);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:25:18
|
||||
|
|
||||
25 | uri!(simple: name = 100, id = 100);
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `simple` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:26:18
|
||||
|
|
||||
26 | uri!(simple: id = 100, id = 100, );
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:26:28
|
||||
|
|
||||
26 | uri!(simple: id = 100, id = 100, );
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `simple` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:27:18
|
||||
|
|
||||
27 | uri!(simple: name = "hi");
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:27:18
|
||||
|
|
||||
27 | uri!(simple: name = "hi");
|
||||
| ^^^^
|
||||
= help: missing parameter: `id`
|
||||
|
||||
error: invalid parameters for `guard_1` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:28:19
|
||||
|
|
||||
28 | uri!(guard_1: cookies = "hi", id = 100);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:28:19
|
||||
|
|
||||
28 | uri!(guard_1: cookies = "hi", id = 100);
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `guard_1` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:29:19
|
||||
|
|
||||
29 | uri!(guard_1: id = 100, cookies = "hi");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:29:29
|
||||
|
|
||||
29 | uri!(guard_1: id = 100, cookies = "hi");
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn simple(id: i32, name: String) -> &'static str { "" }
|
||||
|
||||
fn main() {
|
||||
uri!(simple: id = 100, "Hello");
|
||||
uri!(simple: "Hello", id = 100);
|
||||
uri!(simple,);
|
||||
uri!(simple:);
|
||||
uri!("/mount");
|
||||
uri!("/mount",);
|
||||
uri!("mount", simple);
|
||||
uri!("/mount/<id>", simple);
|
||||
uri!();
|
||||
uri!(simple: id = );
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
error: named and unnamed parameters cannot be mixed
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:11:18
|
||||
|
|
||||
11 | uri!(simple: id = 100, "Hello");
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named and unnamed parameters cannot be mixed
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:12:18
|
||||
|
|
||||
12 | uri!(simple: "Hello", id = 100);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `:`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:13:16
|
||||
|
|
||||
13 | uri!(simple,);
|
||||
| ^
|
||||
|
||||
error: expected argument list after `:`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:14:16
|
||||
|
|
||||
14 | uri!(simple:);
|
||||
| ^
|
||||
|
||||
error: unexpected end of input: expected ',' followed by route path
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:15:10
|
||||
|
|
||||
15 | uri!("/mount");
|
||||
| ^^^^^^^^
|
||||
|
||||
error: unexpected end of input, expected identifier
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:16:5
|
||||
|
|
||||
16 | uri!("/mount",);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: invalid mount point; mount points must be static, absolute URIs: `/example`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:17:10
|
||||
|
|
||||
17 | uri!("mount", simple);
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid mount point; mount points must be static, absolute URIs: `/example`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:18:10
|
||||
|
|
||||
18 | uri!("/mount/<id>", simple);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: unexpected end of input, call to `uri!` cannot be empty
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:19:5
|
||||
|
|
||||
19 | uri!();
|
||||
| ^^^^^^^
|
||||
|
||||
error: unexpected end of input, expected expression
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:20:5
|
||||
|
|
||||
20 | uri!(simple: id = );
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
|
@ -23,9 +23,8 @@ rocket_http = { version = "0.4.0-dev", path = "../http/" }
|
|||
|
||||
[dependencies.derive_utils]
|
||||
git = "https://github.com/SergioBenitez/derive-utils"
|
||||
rev = "f14fb4bc855"
|
||||
rev = "62f361f"
|
||||
|
||||
[dev-dependencies]
|
||||
rocket = { version = "0.4.0-dev", path = "../lib" }
|
||||
rocket_codegen = { version = "0.4.0-dev", path = "../codegen" }
|
||||
compiletest_rs = "0.3.14"
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use proc_macro::{TokenStream, Span};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use derive_utils::{syn, Spanned, Result, FromMeta, ext::TypeExt};
|
||||
use derive_utils::{syn, Spanned, SpanWrapped, Result, FromMeta, ext::TypeExt};
|
||||
use indexmap::IndexSet;
|
||||
|
||||
use proc_macro_ext::Diagnostics;
|
||||
use proc_macro_ext::{Diagnostics, SpanExt};
|
||||
use syn_ext::{syn_to_diag, IdentExt};
|
||||
use self::syn::{Attribute, parse::Parser};
|
||||
|
||||
|
@ -15,9 +15,9 @@ use {ROUTE_FN_PREFIX, ROUTE_STRUCT_PREFIX, URI_MACRO_PREFIX, ROCKET_PARAM_PREFIX
|
|||
#[derive(Debug, FromMeta)]
|
||||
struct RouteAttribute {
|
||||
#[meta(naked)]
|
||||
method: Method,
|
||||
method: SpanWrapped<Method>,
|
||||
path: RoutePath,
|
||||
data: Option<DataSegment>,
|
||||
data: Option<SpanWrapped<DataSegment>>,
|
||||
format: Option<MediaType>,
|
||||
rank: Option<isize>,
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ struct RouteAttribute {
|
|||
struct MethodRouteAttribute {
|
||||
#[meta(naked)]
|
||||
path: RoutePath,
|
||||
data: Option<DataSegment>,
|
||||
data: Option<SpanWrapped<DataSegment>>,
|
||||
format: Option<MediaType>,
|
||||
rank: Option<isize>,
|
||||
}
|
||||
|
@ -51,6 +51,16 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
|||
// Gather diagnostics as we proceed.
|
||||
let mut diags = Diagnostics::new();
|
||||
|
||||
// Emit a warning if a `data` param was supplied for non-payload methods.
|
||||
if let Some(ref data) = attr.data {
|
||||
if !attr.method.0.supports_payload() {
|
||||
let msg = format!("'{}' does not typically support payloads", attr.method.0);
|
||||
data.full_span.warning("`data` used with non-payload-supporting method")
|
||||
.span_note(attr.method.span, msg)
|
||||
.emit()
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all of the dynamic segments in an `IndexSet`, checking for dups.
|
||||
let mut segments: IndexSet<Segment> = IndexSet::new();
|
||||
fn dup_check<I>(set: &mut IndexSet<Segment>, iter: I, diags: &mut Diagnostics)
|
||||
|
@ -67,7 +77,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
|||
|
||||
dup_check(&mut segments, attr.path.path.iter().cloned(), &mut diags);
|
||||
attr.path.query.as_ref().map(|q| dup_check(&mut segments, q.iter().cloned(), &mut diags));
|
||||
dup_check(&mut segments, attr.data.clone().map(|s| s.0).into_iter(), &mut diags);
|
||||
dup_check(&mut segments, attr.data.clone().map(|s| s.value.0).into_iter(), &mut diags);
|
||||
|
||||
// Check the validity of function arguments.
|
||||
let mut inputs = vec![];
|
||||
|
@ -100,7 +110,11 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
|||
}
|
||||
|
||||
// Check that all of the declared parameters are function inputs.
|
||||
let span = function.decl.inputs.span();
|
||||
let span = match function.decl.inputs.is_empty() {
|
||||
false => function.decl.inputs.span(),
|
||||
true => function.span()
|
||||
};
|
||||
|
||||
for missing in segments.difference(&fn_segments) {
|
||||
diags.push(missing.span.error("unused dynamic parameter")
|
||||
.span_note(span, format!("expected argument named `{}` here", missing.name)))
|
||||
|
@ -210,13 +224,16 @@ fn query_exprs(route: &Route) -> Option<TokenStream2> {
|
|||
let matcher = match segment.kind {
|
||||
Kind::Single => quote_spanned! { span =>
|
||||
(_, #name, __v) => {
|
||||
#ident = Some(match <#ty as FromFormValue>::from_form_value(__v) {
|
||||
#[allow(unreachable_patterns, unreachable_code)]
|
||||
let __v = match <#ty as FromFormValue>::from_form_value(__v) {
|
||||
Ok(__v) => __v,
|
||||
Err(__e) => {
|
||||
log_warn_(&format!("Failed to parse '{}': {:?}", #name, __e));
|
||||
return Outcome::Forward(__data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
#ident = Some(__v);
|
||||
}
|
||||
},
|
||||
Kind::Static => quote! {
|
||||
|
@ -408,7 +425,9 @@ fn incomplete_route(
|
|||
input: TokenStream
|
||||
) -> Result<TokenStream> {
|
||||
let method_str = method.to_string().to_lowercase();
|
||||
let method_ident = syn::Ident::new(&method_str, args.span().into());
|
||||
// FIXME(proc_macro): there should be a way to get this `Span`.
|
||||
let method_span = Span::call_site().subspan(2..2 + method_str.len()).unwrap();
|
||||
let method_ident = syn::Ident::new(&method_str, method_span.into());
|
||||
|
||||
let function: syn::ItemFn = syn::parse(input).map_err(syn_to_diag)
|
||||
.map_err(|d| d.help(format!("#[{}] can only be used on functions", method_str)))?;
|
||||
|
@ -421,7 +440,9 @@ fn incomplete_route(
|
|||
};
|
||||
|
||||
let attribute = RouteAttribute {
|
||||
method: Method(method),
|
||||
method: SpanWrapped {
|
||||
full_span: method_span, span: method_span, value: Method(method)
|
||||
},
|
||||
path: method_attribute.path,
|
||||
data: method_attribute.data,
|
||||
format: method_attribute.format,
|
||||
|
|
|
@ -52,13 +52,16 @@ impl Hash for Segment {
|
|||
|
||||
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)
|
||||
span.subspan(index..index + needle.len())
|
||||
}
|
||||
|
||||
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)
|
||||
if needle.as_ptr() as usize > haystack.as_ptr() as usize {
|
||||
span.subspan((index - 1)..)
|
||||
} else {
|
||||
span.subspan(index..)
|
||||
}
|
||||
}
|
||||
|
||||
fn into_diagnostic(
|
||||
|
@ -67,7 +70,7 @@ fn into_diagnostic(
|
|||
span: Span, // The `Span` of `Source`.
|
||||
error: &Error, // The error.
|
||||
) -> Diagnostic {
|
||||
let seg_span = subspan(segment, source, span).unwrap();
|
||||
let seg_span = subspan(segment, source, span).expect("seg_span");
|
||||
match error {
|
||||
Error::Empty => {
|
||||
seg_span.error("parameter names cannot be empty")
|
||||
|
@ -94,8 +97,8 @@ fn into_diagnostic(
|
|||
.note("components cannot contain '%' and '+' characters")
|
||||
}
|
||||
Error::Trailing(multi) => {
|
||||
let multi_span = subspan(multi, source, span).unwrap();
|
||||
trailspan(segment, source, span).unwrap()
|
||||
let multi_span = subspan(multi, source, span).expect("mutli_span");
|
||||
trailspan(segment, source, span).expect("trailspan")
|
||||
.error("unexpected trailing text after a '..' param")
|
||||
.help("a multi-segment param must be the final component")
|
||||
.span_note(multi_span, "multi-segment param is here")
|
||||
|
@ -125,7 +128,7 @@ crate fn parse_segments(
|
|||
break;
|
||||
}
|
||||
} else if let Ok(segment) = result {
|
||||
let seg_span = subspan(&segment.string, string, span).unwrap();
|
||||
let seg_span = subspan(&segment.string, string, span).expect("seg");
|
||||
segments.push(Segment::from(segment, seg_span));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,10 +54,16 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> {
|
|||
.note(format!("uri parameters are: {}", internal.fn_args_str()));
|
||||
|
||||
fn join<S: Display, T: Iterator<Item = S>>(iter: T) -> (&'static str, String) {
|
||||
let items: Vec<_> = iter.map(|i| format!("`{}`", i)).collect();
|
||||
let mut items: Vec<_> = iter.map(|i| format!("`{}`", i)).collect();
|
||||
items.dedup();
|
||||
(p!("parameter", items.len()), items.join(", "))
|
||||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
let (ps, msg) = join(missing.iter());
|
||||
diag = diag.help(format!("missing {}: {}", ps, msg));
|
||||
}
|
||||
|
||||
if !extra.is_empty() {
|
||||
let (ps, msg) = join(extra.iter());
|
||||
let spans: Vec<_> = extra.iter().map(|ident| ident.span().unstable()).collect();
|
||||
|
@ -70,11 +76,6 @@ fn extract_exprs(internal: &InternalUriParams) -> Result<Vec<&Expr>> {
|
|||
diag = diag.span_help(spans, format!("duplicate {}: {}", ps, msg));
|
||||
}
|
||||
|
||||
if !missing.is_empty() {
|
||||
let (ps, msg) = join(missing.iter());
|
||||
diag = diag.help(format!("missing {}: {}", ps, msg));
|
||||
}
|
||||
|
||||
Err(diag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,16 @@ impl ToTokens for ContentType {
|
|||
|
||||
impl FromMeta for MediaType {
|
||||
fn from_meta(meta: MetaItem) -> Result<Self> {
|
||||
http::MediaType::parse_flexible(&String::from_meta(meta)?)
|
||||
.map(MediaType)
|
||||
.ok_or(meta.value_span().error("invalid or unknown media type"))
|
||||
let mt = http::MediaType::parse_flexible(&String::from_meta(meta)?)
|
||||
.ok_or(meta.value_span().error("invalid or unknown media type"))?;
|
||||
|
||||
if !mt.is_known() {
|
||||
meta.value_span()
|
||||
.warning(format!("'{}' is not a known media type", mt))
|
||||
.emit();
|
||||
}
|
||||
|
||||
Ok(MediaType(mt))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +165,7 @@ impl FromMeta for Origin {
|
|||
let uri = http::uri::Origin::parse_route(&string)
|
||||
.map_err(|e| {
|
||||
let span = e.index()
|
||||
.map(|i| span.trimmed(i + 1, 0).unwrap())
|
||||
.map(|i| span.subspan(i + 1..).expect("origin"))
|
||||
.unwrap_or(span);
|
||||
|
||||
span.error(format!("invalid path URI: {}", e))
|
||||
|
@ -178,7 +185,9 @@ impl FromMeta for Origin {
|
|||
impl FromMeta for Segment {
|
||||
fn from_meta(meta: MetaItem) -> Result<Self> {
|
||||
let string = String::from_meta(meta)?;
|
||||
let span = meta.value_span().trimmed(1, 1).unwrap();
|
||||
let span = meta.value_span()
|
||||
.subspan(1..(string.len() + 1))
|
||||
.expect("segment");
|
||||
|
||||
let segment = parse_segment(&string, span)?;
|
||||
if segment.kind != Kind::Single {
|
||||
|
@ -201,20 +210,18 @@ impl FromMeta for DataSegment {
|
|||
|
||||
impl FromMeta for RoutePath {
|
||||
fn from_meta(meta: MetaItem) -> Result<Self> {
|
||||
let origin = Origin::from_meta(meta)?;
|
||||
|
||||
let span = meta.value_span().trimmed(1, 1).unwrap();
|
||||
let query_len = origin.0.query().map(|q| q.len() + 1).unwrap_or(0);
|
||||
let path_span = span.trimmed(0, query_len).unwrap();
|
||||
let (origin, span) = (Origin::from_meta(meta)?, meta.value_span());
|
||||
let path_span = span.subspan(1..origin.0.path().len() + 1).expect("path");
|
||||
let path = parse_segments(origin.0.path(), '/', Source::Path, path_span);
|
||||
|
||||
let query = origin.0.query()
|
||||
.map(|q| {
|
||||
let len_to_q = origin.0.path().len() + 1;
|
||||
let query_span = span.trimmed(len_to_q, 0).unwrap();
|
||||
let len_to_q = 1 + origin.0.path().len() + 1;
|
||||
let end_of_q = len_to_q + q.len();
|
||||
let query_span = span.subspan(len_to_q..end_of_q).expect("query");
|
||||
if q.starts_with('&') || q.contains("&&") || q.ends_with('&') {
|
||||
// TODO: Show a help message with what's expected.
|
||||
Err(query_span.error("query cannot contain empty components").into())
|
||||
Err(query_span.error("query cannot contain empty segments").into())
|
||||
} else {
|
||||
parse_segments(q, '&', Source::Query, query_span)
|
||||
}
|
||||
|
|
|
@ -14,11 +14,15 @@
|
|||
//! ## **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
|
||||
|
@ -35,11 +39,13 @@
|
|||
//! * **options**
|
||||
//! * **catch**
|
||||
//!
|
||||
//! ### Route Attributes
|
||||
//!
|
||||
//! The grammar for all _route_ attributes, including **route**, **get**,
|
||||
//! **put**, **post**, **delete**, **head**, **patch**, and **options** is
|
||||
//! defined as:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! route := METHOD? '(' ('path' '=')? path (',' kv_param)* ')'
|
||||
//!
|
||||
//! path := URI_SEG
|
||||
|
@ -58,26 +64,34 @@
|
|||
//!
|
||||
//! 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")]
|
||||
//! ```rust,ignore
|
||||
//! #[route(GET, path = "/hello")]
|
||||
//! ```
|
||||
//!
|
||||
//! while the equivalent using **get** looks like:
|
||||
//!
|
||||
//! #[get("/hello")]
|
||||
//! ```rust,ignore
|
||||
//! #[get("/hello")]
|
||||
//! ```
|
||||
//!
|
||||
//! ### Catch Attribute
|
||||
//!
|
||||
//! The syntax for the **catch** attribute is:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! catch := INTEGER
|
||||
//! </pre>
|
||||
//! ```
|
||||
//!
|
||||
//! A use of the `catch` attribute looks like:
|
||||
//!
|
||||
//! #[catch(404)]
|
||||
//! ```rust,ignore
|
||||
//! #[catch(404)]
|
||||
//! ```
|
||||
//!
|
||||
//! ## Custom Derives
|
||||
//!
|
||||
|
@ -87,37 +101,43 @@
|
|||
//! * **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>
|
||||
//! <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
|
||||
//! }
|
||||
//! ```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:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! 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
|
||||
//! }
|
||||
//! ```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
|
||||
|
@ -137,12 +157,14 @@
|
|||
//! The [`FromFormValue`] derive can be applied to enums with nullary
|
||||
//! (zero-length) fields:
|
||||
//!
|
||||
//! #[derive(FromFormValue)]
|
||||
//! enum MyValue {
|
||||
//! First,
|
||||
//! Second,
|
||||
//! Third,
|
||||
//! }
|
||||
//! ```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
|
||||
|
@ -157,21 +179,23 @@
|
|||
//! 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,
|
||||
//! }
|
||||
//! ```rust,ignore
|
||||
//! #[derive(FromFormValue)]
|
||||
//! enum MyValue {
|
||||
//! First,
|
||||
//! Second,
|
||||
//! #[form(value = "fourth")]
|
||||
//! Third,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The attribute's grammar is:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! 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
|
||||
|
@ -184,17 +208,19 @@
|
|||
//! 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),
|
||||
//! }
|
||||
//! ```rust,ignore
|
||||
//! #[derive(Responder)]
|
||||
//! enum MyResponder {
|
||||
//! A(String),
|
||||
//! B(OtherResponse, ContentType),
|
||||
//! }
|
||||
//!
|
||||
//! #[derive(Responder)]
|
||||
//! struct MyResponder {
|
||||
//! inner: OtherResponder,
|
||||
//! header: 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
|
||||
|
@ -207,19 +233,21 @@
|
|||
//! 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),
|
||||
//! }
|
||||
//! ```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,
|
||||
//! }
|
||||
//! #[derive(Responder)]
|
||||
//! struct MyResponder {
|
||||
//! inner: InnerResponder,
|
||||
//! header: ContentType,
|
||||
//! #[response(ignore)]
|
||||
//! other: Other,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Decorating the first field with `#[response(ignore)]` has no effect.
|
||||
//!
|
||||
|
@ -228,7 +256,7 @@
|
|||
//! produced by the generated implementation. The `response` attribute used in
|
||||
//! these positions has the following grammar:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! response := parameter (',' parameter)?
|
||||
//!
|
||||
//! parameter := 'status' '=' STATUS
|
||||
|
@ -237,26 +265,28 @@
|
|||
//! 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),
|
||||
//! }
|
||||
//! ```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,
|
||||
//! }
|
||||
//! #[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
|
||||
|
@ -288,13 +318,15 @@
|
|||
//! * **catchers**
|
||||
//! * **uri**
|
||||
//!
|
||||
//! ## Routes and Catchers
|
||||
//!
|
||||
//! The syntax for `routes!` and `catchers!` is defined as:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! macro := PATH (',' PATH)*
|
||||
//!
|
||||
//! PATH := a path, as defined by Rust
|
||||
//! </pre>
|
||||
//! ```
|
||||
//!
|
||||
//! ### Typed URIs: `uri!`
|
||||
//!
|
||||
|
@ -330,7 +362,7 @@
|
|||
//!
|
||||
//! The grammar for the `uri!` macro is as follows:
|
||||
//!
|
||||
//! <pre>
|
||||
//! ```text
|
||||
//! uri := (mount ',')? PATH (':' params)?
|
||||
//!
|
||||
//! mount = STRING
|
||||
|
@ -342,7 +374,7 @@
|
|||
//! 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
|
||||
//!
|
||||
|
@ -361,7 +393,7 @@
|
|||
//! 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
|
||||
//! ```
|
||||
//!
|
||||
|
@ -383,7 +415,7 @@
|
|||
//! might run the following to build a Rocket application with codegen logging
|
||||
//! enabled:
|
||||
//!
|
||||
//! ```
|
||||
//! ```sh
|
||||
//! ROCKET_CODEGEN_DEBUG=1 cargo build
|
||||
//! ```
|
||||
|
||||
|
@ -413,11 +445,25 @@ crate static ROUTE_FN_PREFIX: &str = "rocket_route_fn_";
|
|||
crate static URI_MACRO_PREFIX: &str = "rocket_uri_macro_";
|
||||
crate static ROCKET_PARAM_PREFIX: &str = "__rocket_param_";
|
||||
|
||||
macro_rules! emit {
|
||||
($tokens:expr) => ({
|
||||
let tokens = $tokens;
|
||||
if ::std::env::var_os("ROCKET_CODEGEN_DEBUG").is_some() {
|
||||
::proc_macro::Span::call_site()
|
||||
.note("emitting Rocket code generation debug output")
|
||||
.note(tokens.to_string())
|
||||
.emit()
|
||||
}
|
||||
|
||||
tokens
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! route_attribute {
|
||||
($name:ident => $method:expr) => (
|
||||
#[proc_macro_attribute]
|
||||
pub fn $name(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
attribute::route::route_attribute($method, args, input)
|
||||
emit!(attribute::route::route_attribute($method, args, input))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -433,40 +479,41 @@ route_attribute!(options => Method::Options);
|
|||
|
||||
#[proc_macro_derive(FromFormValue, attributes(form))]
|
||||
pub fn derive_from_form_value(input: TokenStream) -> TokenStream {
|
||||
derive::from_form_value::derive_from_form_value(input)
|
||||
emit!(derive::from_form_value::derive_from_form_value(input))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromForm, attributes(form))]
|
||||
pub fn derive_from_form(input: TokenStream) -> TokenStream {
|
||||
derive::from_form::derive_from_form(input)
|
||||
emit!(derive::from_form::derive_from_form(input))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Responder, attributes(response))]
|
||||
pub fn derive_responder(input: TokenStream) -> TokenStream {
|
||||
derive::responder::derive_responder(input)
|
||||
emit!(derive::responder::derive_responder(input))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn catch(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
attribute::catch::catch_attribute(args, input)
|
||||
emit!(attribute::catch::catch_attribute(args, input))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn routes(input: TokenStream) -> TokenStream {
|
||||
bang::routes_macro(input)
|
||||
emit!(bang::routes_macro(input))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn catchers(input: TokenStream) -> TokenStream {
|
||||
bang::catchers_macro(input)
|
||||
emit!(bang::catchers_macro(input))
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn uri(input: TokenStream) -> TokenStream {
|
||||
bang::uri_macro(input)
|
||||
emit!(bang::uri_macro(input))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[proc_macro]
|
||||
pub fn rocket_internal_uri(input: TokenStream) -> TokenStream {
|
||||
bang::uri_internal_macro(input)
|
||||
emit!(bang::uri_internal_macro(input))
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
use proc_macro::{Span, Diagnostic, /* MultiSpan */};
|
||||
use syntax_pos::{Span as InnerSpan, Pos, BytePos};
|
||||
|
||||
|
@ -61,37 +63,40 @@ impl From<Vec<Diagnostic>> for Diagnostics {
|
|||
}
|
||||
|
||||
pub trait SpanExt {
|
||||
fn trimmed(&self, left: usize, right: usize) -> Option<Span>;
|
||||
fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span>;
|
||||
}
|
||||
|
||||
impl SpanExt for Span {
|
||||
/// Trim the span on the left by `left` characters and on the right by
|
||||
/// `right` characters.
|
||||
fn trimmed(&self, left: usize, right: usize) -> Option<Span> {
|
||||
let inner: InnerSpan = unsafe { ::std::mem::transmute(*self) };
|
||||
if left > u32::max_value() as usize || right > u32::max_value() as usize {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ensure that the addition won't overflow.
|
||||
let (left, right) = (left as u32, right as u32);
|
||||
if u32::max_value() - left < inner.lo().to_u32() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ensure that the subtraction won't underflow.
|
||||
if right > inner.hi().to_u32() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let new_lo = inner.lo() + BytePos(left);
|
||||
let new_hi = inner.hi() - BytePos(right);
|
||||
|
||||
// Ensure we're still inside the old `Span` and didn't cross paths.
|
||||
if new_lo >= new_hi {
|
||||
/// Create a `subspan` from `start` to `end`.
|
||||
fn subspan<R: RangeBounds<usize>>(self, range: R) -> Option<Span> {
|
||||
let inner: InnerSpan = unsafe { ::std::mem::transmute(self) };
|
||||
let length = inner.hi().to_usize() - inner.lo().to_usize();
|
||||
|
||||
let start = match range.start_bound() {
|
||||
Bound::Included(&lo) => lo,
|
||||
Bound::Excluded(&lo) => lo + 1,
|
||||
Bound::Unbounded => 0,
|
||||
};
|
||||
|
||||
let end = match range.end_bound() {
|
||||
Bound::Included(&hi) => hi + 1,
|
||||
Bound::Excluded(&hi) => hi,
|
||||
Bound::Unbounded => length,
|
||||
};
|
||||
|
||||
// Bounds check the values, preventing addition overflow and OOB spans.
|
||||
if start > u32::max_value() as usize
|
||||
|| end > u32::max_value() as usize
|
||||
|| (u32::max_value() - start as u32) < inner.lo().to_u32()
|
||||
|| (u32::max_value() - end as u32) < inner.lo().to_u32()
|
||||
|| start >= end
|
||||
|| end > length
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let new_lo = inner.lo() + BytePos(start as u32);
|
||||
let new_hi = inner.lo() + BytePos(end as u32);
|
||||
let new_inner = inner.with_lo(new_lo).with_hi(new_hi);
|
||||
Some(unsafe { ::std::mem::transmute(new_inner) })
|
||||
}
|
||||
|
|
|
@ -90,7 +90,6 @@ fn run_mode(mode: &'static str, path: &'static str) {
|
|||
config.target_rustcflags = Some([
|
||||
link_flag("-L", "crate", &[]),
|
||||
link_flag("-L", "dependency", &["deps"]),
|
||||
extern_dep("rocket_codegen_next", Kind::Dynamic).expect("find codegen dep"),
|
||||
extern_dep("rocket_http", Kind::Static).expect("find http dep"),
|
||||
extern_dep("rocket", Kind::Static).expect("find core dep"),
|
||||
].join(" "));
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::local::Client;
|
||||
|
|
|
@ -27,10 +27,10 @@ fn msgpack() -> &'static str { "msgpack" }
|
|||
#[get("/", format = "plain")]
|
||||
fn plain() -> &'static str { "plain" }
|
||||
|
||||
#[get("/", format = "binary")]
|
||||
#[get("/", format = "binary", rank = 2)]
|
||||
fn binary() -> &'static str { "binary" }
|
||||
|
||||
#[get("/", rank = 2)]
|
||||
#[get("/", rank = 3)]
|
||||
fn other() -> &'static str { "other" }
|
||||
|
||||
#[test]
|
||||
|
@ -57,10 +57,13 @@ fn test_formats() {
|
|||
assert_eq!(response.body_string().unwrap(), "binary");
|
||||
|
||||
let mut response = client.get("/").header(ContentType::JSON).dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "other");
|
||||
assert_eq!(response.body_string().unwrap(), "plain");
|
||||
|
||||
let mut response = client.get("/").dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "other");
|
||||
assert_eq!(response.body_string().unwrap(), "plain");
|
||||
|
||||
let response = client.put("/").header(ContentType::HTML).dispatch();
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
}
|
||||
|
||||
// Test custom formats.
|
||||
|
@ -71,7 +74,7 @@ fn get_foo() -> &'static str { "get_foo" }
|
|||
#[post("/", format = "application/foo")]
|
||||
fn post_foo() -> &'static str { "post_foo" }
|
||||
|
||||
#[get("/", format = "bar/baz")]
|
||||
#[get("/", format = "bar/baz", rank = 2)]
|
||||
fn get_bar_baz() -> &'static str { "get_bar_baz" }
|
||||
|
||||
#[put("/", format = "bar/baz")]
|
||||
|
@ -101,6 +104,12 @@ fn test_custom_formats() {
|
|||
let mut response = client.put("/").header(bar_baz_ct).dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "put_bar_baz");
|
||||
|
||||
let response = client.get("/").dispatch();
|
||||
let mut response = client.get("/").dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "get_foo");
|
||||
|
||||
let response = client.put("/").header(ContentType::HTML).dispatch();
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
|
||||
let response = client.post("/").header(ContentType::HTML).dispatch();
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
}
|
||||
|
|
|
@ -119,3 +119,33 @@ fn test_full_route() {
|
|||
assert_eq!(response.body_string().unwrap(), format!("({}, {}, {}, {}, {}, {}) ({})",
|
||||
sky, name, "A A", "inside", path, simple, expected_uri));
|
||||
}
|
||||
|
||||
// Check that we propogate span information correctly to allow re-expansion.
|
||||
|
||||
#[get("/easy/<id>")]
|
||||
fn easy(id: i32) -> String {
|
||||
format!("easy id: {}", id)
|
||||
}
|
||||
|
||||
macro_rules! make_handler {
|
||||
() => {
|
||||
#[get("/hard/<id>")]
|
||||
fn hard(id: i32) -> String {
|
||||
format!("hard id: {}", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_handler!();
|
||||
|
||||
#[test]
|
||||
fn test_reexpansion() {
|
||||
let rocket = rocket::ignite().mount("/", routes![easy, hard]);
|
||||
let client = Client::new(rocket).unwrap();
|
||||
|
||||
let mut response = client.get("/easy/327").dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "easy id: 327");
|
||||
|
||||
let mut response = client.get("/hard/72").dispatch();
|
||||
assert_eq!(response.body_string().unwrap(), "hard id: 72");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
@ -42,47 +41,56 @@ struct Second {
|
|||
}
|
||||
|
||||
#[post("/<id>")]
|
||||
fn simple(id: i32) -> &'static str { "" }
|
||||
fn simple(id: i32) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn simple2(id: i32, name: String) -> &'static str { "" }
|
||||
fn simple2(id: i32, name: String) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn simple2_flipped(name: String, id: i32) -> &'static str { "" }
|
||||
fn simple2_flipped(name: String, id: i32) { }
|
||||
|
||||
#[post("/?<id>")]
|
||||
fn simple3(id: i32) { }
|
||||
|
||||
#[post("/?<id>&<name>")]
|
||||
fn simple4(id: i32, name: String) { }
|
||||
|
||||
#[post("/?<id>&<name>")]
|
||||
fn simple4_flipped(name: String, id: i32) { }
|
||||
|
||||
#[post("/<used>/<_unused>")]
|
||||
fn unused_param(used: i32, _unused: i32) -> &'static str { "" }
|
||||
fn unused_param(used: i32, _unused: i32) { }
|
||||
|
||||
#[post("/<id>")]
|
||||
fn guard_1(cookies: Cookies, id: i32) -> &'static str { "" }
|
||||
fn guard_1(cookies: Cookies, id: i32) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn guard_2(name: String, cookies: Cookies, id: i32) -> &'static str { "" }
|
||||
fn guard_2(name: String, cookies: Cookies, id: i32) { }
|
||||
|
||||
#[post("/a/<id>/hi/<name>/hey")]
|
||||
fn guard_3(id: i32, name: String, cookies: Cookies) -> &'static str { "" }
|
||||
fn guard_3(id: i32, name: String, cookies: Cookies) { }
|
||||
|
||||
#[post("/<id>", data = "<form>")]
|
||||
fn no_uri_display_okay(id: i32, form: Form<Second>) -> &'static str {
|
||||
"Typed URI testing."
|
||||
}
|
||||
fn no_uri_display_okay(id: i32, form: Form<Second>) { }
|
||||
|
||||
#[post("/<name>?<query..>", data = "<user>", rank = 2)]
|
||||
#[post("/name/<name>?<foo>&bar=10&<bar>&<query..>", data = "<user>", rank = 2)]
|
||||
fn complex<'r>(
|
||||
foo: usize,
|
||||
name: &RawStr,
|
||||
query: Form<User<'r>>,
|
||||
user: Form<User<'r>>,
|
||||
bar: &RawStr,
|
||||
cookies: Cookies
|
||||
) -> &'static str { "" }
|
||||
) { }
|
||||
|
||||
#[post("/a/<path..>")]
|
||||
fn segments(path: PathBuf) -> &'static str { "" }
|
||||
fn segments(path: PathBuf) { }
|
||||
|
||||
#[post("/a/<id>/then/<path..>")]
|
||||
fn param_and_segments(path: PathBuf, id: usize) -> &'static str { "" }
|
||||
fn param_and_segments(path: PathBuf, id: usize) { }
|
||||
|
||||
#[post("/a/<id>/then/<path..>")]
|
||||
fn guarded_segments(cookies: Cookies, path: PathBuf, id: usize) -> &'static str { "" }
|
||||
fn guarded_segments(cookies: Cookies, path: PathBuf, id: usize) { }
|
||||
|
||||
macro assert_uri_eq($($uri:expr => $expected:expr,)+) {
|
||||
$(assert_eq!($uri, Origin::parse($expected).expect("valid origin URI"));)+
|
||||
|
@ -115,6 +123,16 @@ fn check_simple_unnamed() {
|
|||
uri!(simple2: 100, "hello there") => "/100/hello%20there",
|
||||
uri!(simple2_flipped: 100, "hello there") => "/100/hello%20there",
|
||||
}
|
||||
|
||||
// Ensure that query parameters are handled properly.
|
||||
assert_uri_eq! {
|
||||
uri!(simple3: 100) => "/?id=100",
|
||||
uri!(simple3: 1349) => "/?id=1349",
|
||||
uri!(simple4: 100, "bob") => "/?id=100&name=bob",
|
||||
uri!(simple4: 1349, "Bob Anderson") => "/?id=1349&name=Bob%20Anderson",
|
||||
uri!(simple4_flipped: 100, "bob") => "/?id=100&name=bob",
|
||||
uri!(simple4_flipped: 1349, "Bob Anderson") => "/?id=1349&name=Bob%20Anderson",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -149,6 +167,17 @@ fn check_simple_named() {
|
|||
uri!(simple2_flipped: id = 100, name = "hello there") => "/100/hello%20there",
|
||||
uri!(simple2_flipped: name = "hello there", id = 100) => "/100/hello%20there",
|
||||
}
|
||||
|
||||
// Ensure that query parameters are handled properly.
|
||||
assert_uri_eq! {
|
||||
uri!(simple3: id = 100) => "/?id=100",
|
||||
uri!(simple3: id = 1349) => "/?id=1349",
|
||||
uri!(simple4: id = 100, name = "bob") => "/?id=100&name=bob",
|
||||
uri!(simple4: id = 1349, name = "Bob A") => "/?id=1349&name=Bob%20A",
|
||||
uri!(simple4: name = "Bob A", id = 1349) => "/?id=1349&name=Bob%20A",
|
||||
uri!(simple4_flipped: id = 1349, name = "Bob A") => "/?id=1349&name=Bob%20A",
|
||||
uri!(simple4_flipped: name = "Bob A", id = 1349) => "/?id=1349&name=Bob%20A",
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -214,32 +243,43 @@ fn check_with_segments() {
|
|||
#[test]
|
||||
fn check_complex() {
|
||||
assert_uri_eq! {
|
||||
uri!(complex: "no idea", ("A B C", "a c")) => "/no%20idea?name=A+B+C&nickname=a+c",
|
||||
uri!(complex: "Bob", User { name: "Robert".into(), nickname: "Bob".into() })
|
||||
=> "/Bob?name=Robert&nickname=Bob",
|
||||
uri!(complex: "Bob", &User { name: "Robert".into(), nickname: "Bob".into() })
|
||||
=> "/Bob?name=Robert&nickname=Bob",
|
||||
uri!(complex: "no idea", User { name: "Robert Mike".into(), nickname: "Bob".into() })
|
||||
=> "/no%20idea?name=Robert+Mike&nickname=Bob",
|
||||
uri!("/some/path", complex: "no idea", ("A B C", "a c"))
|
||||
=> "/some/path/no%20idea?name=A+B+C&nickname=a+c",
|
||||
uri!(complex: name = "Bob", query = &User { name: "Robert".into(), nickname: "Bob".into() })
|
||||
=> "/Bob?name=Robert&nickname=Bob",
|
||||
uri!(complex: query = User { name: "Robert".into(), nickname: "Bob".into() }, name = "Bob")
|
||||
=> "/Bob?name=Robert&nickname=Bob",
|
||||
uri!(complex: name = "no idea", query = ("A B C", "a c"))
|
||||
=> "/no%20idea?name=A+B+C&nickname=a+c",
|
||||
uri!(complex: query = ("A B C", "a c"), name = "no idea")
|
||||
=> "/no%20idea?name=A+B+C&nickname=a+c",
|
||||
uri!("/hey", complex: name = "no idea", query = ("A B C", "a c"))
|
||||
=> "/hey/no%20idea?name=A+B+C&nickname=a+c",
|
||||
uri!(complex: "no idea", 10, "high", ("A B C", "a c")) =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: "Bob", 248, "?", User { name: "Robert".into(), nickname: "Bob".into() }) =>
|
||||
"/name/Bob?foo=248&bar=10&bar=%3F&name=Robert&nickname=Bob",
|
||||
uri!(complex: "Bob", 248, "a a", &User { name: "Robert".into(), nickname: "B".into() }) =>
|
||||
"/name/Bob?foo=248&bar=10&bar=a%20a&name=Robert&nickname=B",
|
||||
uri!(complex: "no idea", 248, "", &User { name: "A B".into(), nickname: "A".into() }) =>
|
||||
"/name/no%20idea?foo=248&bar=10&bar=&name=A+B&nickname=A",
|
||||
uri!(complex: "hi", 3, "b", &User { name: "A B C".into(), nickname: "a b".into() }) =>
|
||||
"/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b",
|
||||
uri!(complex: name = "no idea", foo = 10, bar = "high", query = ("A B C", "a c")) =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: foo = 10, name = "no idea", bar = "high", query = ("A B C", "a c")) =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: query = ("A B C", "a c"), foo = 10, name = "no idea", bar = "high", ) =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: query = ("A B C", "a c"), foo = 10, name = "no idea", bar = "high") =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: query = *&("A B C", "a c"), foo = 10, name = "no idea", bar = "high") =>
|
||||
"/name/no%20idea?foo=10&bar=10&bar=high&name=A+B+C&nickname=a+c",
|
||||
uri!(complex: foo = 3, name = "hi", bar = "b",
|
||||
query = &User { name: "A B C".into(), nickname: "a b".into() }) =>
|
||||
"/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b",
|
||||
uri!(complex: query = &User { name: "A B C".into(), nickname: "a b".into() },
|
||||
foo = 3, name = "hi", bar = "b") =>
|
||||
"/name/hi?foo=3&bar=10&bar=b&name=A+B+C&nickname=a+b",
|
||||
}
|
||||
|
||||
// Ensure variables are correctly processed.
|
||||
let user = User { name: "Robert".into(), nickname: "Bob".into() };
|
||||
assert_uri_eq! {
|
||||
uri!(complex: "complex", &user) => "/complex?name=Robert&nickname=Bob",
|
||||
uri!(complex: "complex", user) => "/complex?name=Robert&nickname=Bob",
|
||||
uri!(complex: "complex", 0, "high", &user) =>
|
||||
"/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob",
|
||||
uri!(complex: "complex", 0, "high", &user) =>
|
||||
"/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob",
|
||||
uri!(complex: "complex", 0, "high", user) =>
|
||||
"/name/complex?foo=0&bar=10&bar=high&name=Robert&nickname=Bob",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,7 +333,7 @@ mod typed_uris {
|
|||
use super::assert_uri_eq;
|
||||
|
||||
#[post("/typed_uris/<id>")]
|
||||
fn simple(id: i32) -> &'static str { "" }
|
||||
fn simple(id: i32) { }
|
||||
|
||||
#[test]
|
||||
fn check_simple_scoped() {
|
||||
|
@ -309,7 +349,7 @@ mod typed_uris {
|
|||
use super::assert_uri_eq;
|
||||
|
||||
#[post("/typed_uris/deeper/<id>")]
|
||||
fn simple(id: i32) -> &'static str { "" }
|
||||
fn simple(id: i32) { }
|
||||
|
||||
#[test]
|
||||
fn check_deep_scoped() {
|
|
@ -0,0 +1,130 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
// Check a path is supplied, at least.
|
||||
|
||||
#[get()] //~ ERROR missing expected parameter
|
||||
fn a0() {}
|
||||
|
||||
// Check that it only works on functions.
|
||||
|
||||
#[get("/")]
|
||||
struct S;
|
||||
//~^ ERROR expected `fn`
|
||||
//~^^ HELP on functions
|
||||
|
||||
#[get("/")]
|
||||
enum A { }
|
||||
//~^ ERROR expected `fn`
|
||||
//~^^ HELP on functions
|
||||
|
||||
#[get("/")]
|
||||
trait Foo { }
|
||||
//~^ ERROR expected `fn`
|
||||
//~^^ HELP on functions
|
||||
|
||||
#[get("/")]
|
||||
impl S { }
|
||||
//~^ ERROR expected `fn`
|
||||
//~^^ HELP on functions
|
||||
|
||||
// Check that additional parameter weirdness is caught.
|
||||
|
||||
#[get("/", 123)] //~ ERROR expected
|
||||
fn b0() {}
|
||||
|
||||
#[get("/", "/")] //~ ERROR expected
|
||||
fn b1() {}
|
||||
|
||||
#[get(data = "<foo>", "/")] //~ ERROR unexpected keyed parameter
|
||||
fn b2(foo: usize) {}
|
||||
|
||||
#[get("/", unknown = "foo")] //~ ERROR unexpected
|
||||
fn b3() {}
|
||||
|
||||
#[get("/", ...)] //~ ERROR malformed
|
||||
//~^ HELP expected syntax
|
||||
fn b4() {}
|
||||
|
||||
// Check that all identifiers are named
|
||||
|
||||
#[get("/")]
|
||||
fn c1(_: usize) {} //~ ERROR cannot be ignored
|
||||
//~^ HELP must be of the form
|
||||
|
||||
// Check that the path is a string, rank is an integer.
|
||||
|
||||
#[get(100)] //~ ERROR expected string
|
||||
fn d0() {}
|
||||
|
||||
#[get('/')] //~ ERROR expected string
|
||||
fn d1() {}
|
||||
|
||||
#[get("/", rank = "1")] //~ ERROR expected integer
|
||||
fn d2() {}
|
||||
|
||||
#[get("/", rank = '1')] //~ ERROR expected integer
|
||||
fn d3() {}
|
||||
|
||||
// Check that formats are valid media-type strings.
|
||||
|
||||
#[get("/", format = "applicationx-custom")] //~ ERROR invalid or unknown media type
|
||||
fn e0() {}
|
||||
|
||||
#[get("/", format = "")] //~ ERROR invalid or unknown media type
|
||||
fn e1() {}
|
||||
|
||||
#[get("/", format = "//")] //~ ERROR invalid or unknown media type
|
||||
fn e2() {}
|
||||
|
||||
#[get("/", format = "/")] //~ ERROR invalid or unknown media type
|
||||
fn e3() {}
|
||||
|
||||
#[get("/", format = "a/")] //~ ERROR invalid or unknown media type
|
||||
fn e4() {}
|
||||
|
||||
#[get("/", format = "/a")] //~ ERROR invalid or unknown media type
|
||||
fn e5() {}
|
||||
|
||||
#[get("/", format = "/a/")] //~ ERROR invalid or unknown media type
|
||||
fn e6() {}
|
||||
|
||||
#[get("/", format = "a/b/")] //~ ERROR invalid or unknown media type
|
||||
fn e7() {}
|
||||
|
||||
#[get("/", format = "unknown")] //~ ERROR unknown media type
|
||||
fn e8() {}
|
||||
|
||||
#[get("/", format = 12)] //~ ERROR expected string
|
||||
fn e9() {}
|
||||
|
||||
#[get("/", format = 'j')] //~ ERROR expected string
|
||||
fn e10() {}
|
||||
|
||||
#[get("/", format = "text//foo")] //~ ERROR invalid or unknown media type
|
||||
fn e12() {}
|
||||
|
||||
// Check that route methods are validated properly.
|
||||
|
||||
#[route(CONNECT, "/")] //~ ERROR invalid HTTP method for route
|
||||
//~^ HELP method must be one of
|
||||
fn f0() {}
|
||||
|
||||
#[route(FIX, "/")] //~ ERROR invalid HTTP method
|
||||
//~^ HELP method must be one of
|
||||
fn f1() {}
|
||||
|
||||
#[route("hi", "/")] //~ ERROR expected identifier
|
||||
//~^ HELP method must be one of
|
||||
fn f2() {}
|
||||
|
||||
#[route("GET", "/")] //~ ERROR expected identifier
|
||||
//~^ HELP method must be one of
|
||||
fn f3() {}
|
||||
|
||||
#[route(120, "/")] //~ ERROR expected identifier
|
||||
//~^ HELP method must be one of
|
||||
fn f4() {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,216 @@
|
|||
error: missing expected parameter: `path`
|
||||
--> $DIR/route-attribute-general-syntax.rs:7:1
|
||||
|
|
||||
7 | #[get()] //~ ERROR missing expected parameter
|
||||
| ^^^^^^^^
|
||||
|
||||
error: expected `fn`
|
||||
--> $DIR/route-attribute-general-syntax.rs:13:1
|
||||
|
|
||||
13 | struct S;
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: #[get] can only be used on functions
|
||||
|
||||
error: expected `fn`
|
||||
--> $DIR/route-attribute-general-syntax.rs:18:1
|
||||
|
|
||||
18 | enum A { }
|
||||
| ^^^^
|
||||
|
|
||||
= help: #[get] can only be used on functions
|
||||
|
||||
error: expected `fn`
|
||||
--> $DIR/route-attribute-general-syntax.rs:23:1
|
||||
|
|
||||
23 | trait Foo { }
|
||||
| ^^^^^
|
||||
|
|
||||
= help: #[get] can only be used on functions
|
||||
|
||||
error: expected `fn`
|
||||
--> $DIR/route-attribute-general-syntax.rs:28:1
|
||||
|
|
||||
28 | impl S { }
|
||||
| ^^^^
|
||||
|
|
||||
= help: #[get] can only be used on functions
|
||||
|
||||
error: expected key/value pair
|
||||
--> $DIR/route-attribute-general-syntax.rs:34:12
|
||||
|
|
||||
34 | #[get("/", 123)] //~ ERROR expected
|
||||
| ^^^
|
||||
|
||||
error: expected key/value pair
|
||||
--> $DIR/route-attribute-general-syntax.rs:37:12
|
||||
|
|
||||
37 | #[get("/", "/")] //~ ERROR expected
|
||||
| ^^^
|
||||
|
||||
error: unexpected keyed parameter: expected literal or identifier
|
||||
--> $DIR/route-attribute-general-syntax.rs:40:7
|
||||
|
|
||||
40 | #[get(data = "<foo>", "/")] //~ ERROR unexpected keyed parameter
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: unexpected attribute parameter: `unknown`
|
||||
--> $DIR/route-attribute-general-syntax.rs:43:12
|
||||
|
|
||||
43 | #[get("/", unknown = "foo")] //~ ERROR unexpected
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: malformed attribute
|
||||
--> $DIR/route-attribute-general-syntax.rs:46:1
|
||||
|
|
||||
46 | #[get("/", ...)] //~ ERROR malformed
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expected syntax: #[get(key = value, ..)]
|
||||
|
||||
error: handler arguments cannot be ignored
|
||||
--> $DIR/route-attribute-general-syntax.rs:53:7
|
||||
|
|
||||
53 | fn c1(_: usize) {} //~ ERROR cannot be ignored
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: all handler arguments must be of the form: `ident: Type`
|
||||
|
||||
error: invalid value: expected string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:58:7
|
||||
|
|
||||
58 | #[get(100)] //~ ERROR expected string
|
||||
| ^^^
|
||||
|
||||
error: invalid value: expected string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:61:7
|
||||
|
|
||||
61 | #[get('/')] //~ ERROR expected string
|
||||
| ^^^
|
||||
|
||||
error: invalid value: expected integer literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:64:19
|
||||
|
|
||||
64 | #[get("/", rank = "1")] //~ ERROR expected integer
|
||||
| ^^^
|
||||
|
||||
error: invalid value: expected integer literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:67:19
|
||||
|
|
||||
67 | #[get("/", rank = '1')] //~ ERROR expected integer
|
||||
| ^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:72:21
|
||||
|
|
||||
72 | #[get("/", format = "applicationx-custom")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:75:21
|
||||
|
|
||||
75 | #[get("/", format = "")] //~ ERROR invalid or unknown media type
|
||||
| ^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:78:21
|
||||
|
|
||||
78 | #[get("/", format = "//")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:81:21
|
||||
|
|
||||
81 | #[get("/", format = "/")] //~ ERROR invalid or unknown media type
|
||||
| ^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:84:21
|
||||
|
|
||||
84 | #[get("/", format = "a/")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:87:21
|
||||
|
|
||||
87 | #[get("/", format = "/a")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:90:21
|
||||
|
|
||||
90 | #[get("/", format = "/a/")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:93:21
|
||||
|
|
||||
93 | #[get("/", format = "a/b/")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:96:21
|
||||
|
|
||||
96 | #[get("/", format = "unknown")] //~ ERROR unknown media type
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: invalid value: expected string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:99:21
|
||||
|
|
||||
99 | #[get("/", format = 12)] //~ ERROR expected string
|
||||
| ^^
|
||||
|
||||
error: invalid value: expected string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:102:21
|
||||
|
|
||||
102 | #[get("/", format = 'j')] //~ ERROR expected string
|
||||
| ^^^
|
||||
|
||||
error: invalid or unknown media type
|
||||
--> $DIR/route-attribute-general-syntax.rs:105:21
|
||||
|
|
||||
105 | #[get("/", format = "text//foo")] //~ ERROR invalid or unknown media type
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: invalid HTTP method for route handlers
|
||||
--> $DIR/route-attribute-general-syntax.rs:110:9
|
||||
|
|
||||
110 | #[route(CONNECT, "/")] //~ ERROR invalid HTTP method for route
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: invalid HTTP method
|
||||
--> $DIR/route-attribute-general-syntax.rs:114:9
|
||||
|
|
||||
114 | #[route(FIX, "/")] //~ ERROR invalid HTTP method
|
||||
| ^^^
|
||||
|
|
||||
= help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected identifier, found string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:118:9
|
||||
|
|
||||
118 | #[route("hi", "/")] //~ ERROR expected identifier
|
||||
| ^^^^
|
||||
|
|
||||
= help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected identifier, found string literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:122:9
|
||||
|
|
||||
122 | #[route("GET", "/")] //~ ERROR expected identifier
|
||||
| ^^^^^
|
||||
|
|
||||
= help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: expected identifier, found integer literal
|
||||
--> $DIR/route-attribute-general-syntax.rs:126:9
|
||||
|
|
||||
126 | #[route(120, "/")] //~ ERROR expected identifier
|
||||
| ^^^
|
||||
|
|
||||
= help: method must be one of: `GET`, `PUT`, `POST`, `DELETE`, `HEAD`, `PATCH`, `OPTIONS`
|
||||
|
||||
error: aborting due to 32 previous errors
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
// Check that route paths are absolute and normalized.
|
||||
|
||||
#[get("a")] //~ ERROR invalid path URI
|
||||
//~^ HELP expected
|
||||
fn f0() {}
|
||||
|
||||
#[get("")] //~ ERROR invalid path URI
|
||||
//~^ HELP expected
|
||||
fn f1() {}
|
||||
|
||||
#[get("a/b/c")] //~ ERROR invalid path URI
|
||||
//~^ HELP expected
|
||||
fn f2() {}
|
||||
|
||||
#[get("/a///b")] //~ ERROR empty segments
|
||||
//~^ NOTE expected
|
||||
fn f3() {}
|
||||
|
||||
#[get("/?bat&&")] //~ ERROR empty segments
|
||||
fn f4() {}
|
||||
|
||||
#[get("/?bat&&")] //~ ERROR empty segments
|
||||
fn f5() {}
|
||||
|
||||
#[get("/a/b//")] //~ ERROR empty segments
|
||||
//~^ NOTE expected
|
||||
fn f6() {}
|
||||
|
||||
// Check that paths contain only valid URI characters
|
||||
|
||||
#[get("/?????")] //~ ERROR invalid URI characters
|
||||
//~^ NOTE cannot contain
|
||||
fn g0() {}
|
||||
|
||||
#[get("/!@#$%^&*()")] //~ ERROR invalid path URI
|
||||
//~^ HELP origin form
|
||||
fn g1() {}
|
||||
|
||||
#[get("/a%20b")] //~ ERROR invalid URI characters
|
||||
//~^ NOTE cannot contain
|
||||
fn g2() {}
|
||||
|
||||
#[get("/a?a%20b")] //~ ERROR invalid URI characters
|
||||
//~^ NOTE cannot contain
|
||||
fn g3() {}
|
||||
|
||||
#[get("/a?a+b")] //~ ERROR invalid URI characters
|
||||
//~^ NOTE cannot contain
|
||||
fn g4() {}
|
||||
|
||||
// Check that all declared parameters are accounted for
|
||||
|
||||
#[get("/<name>")] //~ ERROR unused dynamic parameter
|
||||
fn h0(_name: usize) {} //~ NOTE expected argument named `name` here
|
||||
|
||||
#[get("/a?<r>")] //~ ERROR unused dynamic parameter
|
||||
fn h1() {} //~ NOTE expected argument named `r` here
|
||||
|
||||
#[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter
|
||||
fn h2() {} //~ NOTE expected argument named `test` here
|
||||
|
||||
#[get("/<_r>")] //~ ERROR unused dynamic parameter
|
||||
fn h3() {} //~ NOTE expected argument named `_r` here
|
||||
|
||||
#[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter
|
||||
//~^ ERROR unused dynamic parameter
|
||||
fn h4() {} //~ NOTE expected argument named `_r` here
|
||||
//~^ NOTE expected argument named `b` here
|
||||
|
||||
// Check dynamic parameters are valid idents
|
||||
|
||||
#[get("/<foo_.>")] //~ ERROR `foo_.` is not a valid identifier
|
||||
//~^ HELP must be valid
|
||||
fn i0() {}
|
||||
|
||||
#[get("/<foo*>")] //~ ERROR `foo*` is not a valid identifier
|
||||
//~^ HELP must be valid
|
||||
fn i1() {}
|
||||
|
||||
#[get("/<!>")] //~ ERROR `!` is not a valid identifier
|
||||
//~^ HELP must be valid
|
||||
fn i2() {}
|
||||
|
||||
#[get("/<name>:<id>")] //~ ERROR `name>:<id` is not a valid identifier
|
||||
//~^ HELP must be valid
|
||||
fn i3() {}
|
||||
|
||||
// Check that a data parameter is exactly `<param>`
|
||||
|
||||
#[get("/", data = "foo")] //~ ERROR malformed parameter
|
||||
//~^ HELP must be of the form
|
||||
fn j0() {}
|
||||
|
||||
#[get("/", data = "<foo..>")] //~ ERROR malformed parameter
|
||||
//~^ HELP must be of the form
|
||||
fn j1() {}
|
||||
|
||||
#[get("/", data = "<foo")] //~ ERROR missing a closing bracket
|
||||
//~^ HELP did you mean
|
||||
fn j2() {}
|
||||
|
||||
#[get("/", data = "<test >")] //~ ERROR `test ` is not a valid identifier
|
||||
//~^ HELP must be valid
|
||||
fn j3() {}
|
||||
|
||||
// Check that all identifiers are named
|
||||
|
||||
#[get("/<_>")] //~ ERROR must be named
|
||||
fn k0(_: usize) {} //~^ HELP use a name such as
|
||||
|
||||
// Check that strange dynamic syntax is caught.
|
||||
|
||||
#[get("/<>")] //~ ERROR cannot be empty
|
||||
fn m0() {}
|
||||
|
||||
#[get("/<id><")] //~ ERROR malformed parameter
|
||||
//~^ HELP must be of the form
|
||||
//~^^ HELP identifiers cannot contain
|
||||
fn m1() {}
|
||||
|
||||
#[get("/<<<<id><")] //~ ERROR malformed parameter
|
||||
//~^ HELP must be of the form
|
||||
//~^^ HELP identifiers cannot contain
|
||||
fn m2() {}
|
||||
|
||||
#[get("/<>name><")] //~ ERROR malformed parameter
|
||||
//~^ HELP must be of the form
|
||||
//~^^ HELP identifiers cannot contain
|
||||
fn m3() {}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,271 @@
|
|||
error: invalid path URI: expected token '/' but found 'a' at index 0
|
||||
--> $DIR/route-path-bad-syntax.rs:7:8
|
||||
|
|
||||
7 | #[get("a")] //~ ERROR invalid path URI
|
||||
| ^^
|
||||
|
|
||||
= help: expected path in origin form: "/path/<param>"
|
||||
|
||||
error: invalid path URI: expected token '/' but none was found at index 0
|
||||
--> $DIR/route-path-bad-syntax.rs:11:8
|
||||
|
|
||||
11 | #[get("")] //~ ERROR invalid path URI
|
||||
| ^
|
||||
|
|
||||
= help: expected path in origin form: "/path/<param>"
|
||||
|
||||
error: invalid path URI: expected token '/' but found 'a' at index 0
|
||||
--> $DIR/route-path-bad-syntax.rs:15:8
|
||||
|
|
||||
15 | #[get("a/b/c")] //~ ERROR invalid path URI
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: expected path in origin form: "/path/<param>"
|
||||
|
||||
error: paths cannot contain empty segments
|
||||
--> $DIR/route-path-bad-syntax.rs:19:7
|
||||
|
|
||||
19 | #[get("/a///b")] //~ ERROR empty segments
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: expected '/a/b', found '/a///b'
|
||||
|
||||
error: query cannot contain empty segments
|
||||
--> $DIR/route-path-bad-syntax.rs:23:10
|
||||
|
|
||||
23 | #[get("/?bat&&")] //~ ERROR empty segments
|
||||
| ^^^^^
|
||||
|
||||
error: query cannot contain empty segments
|
||||
--> $DIR/route-path-bad-syntax.rs:26:10
|
||||
|
|
||||
26 | #[get("/?bat&&")] //~ ERROR empty segments
|
||||
| ^^^^^
|
||||
|
||||
error: paths cannot contain empty segments
|
||||
--> $DIR/route-path-bad-syntax.rs:29:7
|
||||
|
|
||||
29 | #[get("/a/b//")] //~ ERROR empty segments
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: expected '/a/b', found '/a/b//'
|
||||
|
||||
error: component contains invalid URI characters
|
||||
--> $DIR/route-path-bad-syntax.rs:35:10
|
||||
|
|
||||
35 | #[get("/?????")] //~ ERROR invalid URI characters
|
||||
| ^^^^
|
||||
|
|
||||
= note: components cannot contain '%' and '+' characters
|
||||
|
||||
error: invalid path URI: expected EOF but found '#' at index 3
|
||||
--> $DIR/route-path-bad-syntax.rs:39:11
|
||||
|
|
||||
39 | #[get("/!@#$%^&*()")] //~ ERROR invalid path URI
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: expected path in origin form: "/path/<param>"
|
||||
|
||||
error: component contains invalid URI characters
|
||||
--> $DIR/route-path-bad-syntax.rs:43:9
|
||||
|
|
||||
43 | #[get("/a%20b")] //~ ERROR invalid URI characters
|
||||
| ^^^^^
|
||||
|
|
||||
= note: components cannot contain '%' and '+' characters
|
||||
|
||||
error: component contains invalid URI characters
|
||||
--> $DIR/route-path-bad-syntax.rs:47:11
|
||||
|
|
||||
47 | #[get("/a?a%20b")] //~ ERROR invalid URI characters
|
||||
| ^^^^^
|
||||
|
|
||||
= note: components cannot contain '%' and '+' characters
|
||||
|
||||
error: component contains invalid URI characters
|
||||
--> $DIR/route-path-bad-syntax.rs:51:11
|
||||
|
|
||||
51 | #[get("/a?a+b")] //~ ERROR invalid URI characters
|
||||
| ^^^
|
||||
|
|
||||
= note: components cannot contain '%' and '+' characters
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:57:9
|
||||
|
|
||||
57 | #[get("/<name>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^^^^
|
||||
|
|
||||
note: expected argument named `name` here
|
||||
--> $DIR/route-path-bad-syntax.rs:58:7
|
||||
|
|
||||
58 | fn h0(_name: usize) {} //~ NOTE expected argument named `name` here
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:60:11
|
||||
|
|
||||
60 | #[get("/a?<r>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^
|
||||
|
|
||||
note: expected argument named `r` here
|
||||
--> $DIR/route-path-bad-syntax.rs:61:1
|
||||
|
|
||||
61 | fn h1() {} //~ NOTE expected argument named `r` here
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:63:22
|
||||
|
|
||||
63 | #[post("/a", data = "<test>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^^^^
|
||||
|
|
||||
note: expected argument named `test` here
|
||||
--> $DIR/route-path-bad-syntax.rs:64:1
|
||||
|
|
||||
64 | fn h2() {} //~ NOTE expected argument named `test` here
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:66:9
|
||||
|
|
||||
66 | #[get("/<_r>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^^
|
||||
|
|
||||
note: expected argument named `_r` here
|
||||
--> $DIR/route-path-bad-syntax.rs:67:1
|
||||
|
|
||||
67 | fn h3() {} //~ NOTE expected argument named `_r` here
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:69:9
|
||||
|
|
||||
69 | #[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^^
|
||||
|
|
||||
note: expected argument named `_r` here
|
||||
--> $DIR/route-path-bad-syntax.rs:71:1
|
||||
|
|
||||
71 | fn h4() {} //~ NOTE expected argument named `_r` here
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unused dynamic parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:69:14
|
||||
|
|
||||
69 | #[get("/<_r>/<b>")] //~ ERROR unused dynamic parameter
|
||||
| ^^^
|
||||
|
|
||||
note: expected argument named `b` here
|
||||
--> $DIR/route-path-bad-syntax.rs:71:1
|
||||
|
|
||||
71 | fn h4() {} //~ NOTE expected argument named `_r` here
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `foo_.` is not a valid identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:76:9
|
||||
|
|
||||
76 | #[get("/<foo_.>")] //~ ERROR `foo_.` is not a valid identifier
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: parameter names must be valid identifiers
|
||||
|
||||
error: `foo*` is not a valid identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:80:9
|
||||
|
|
||||
80 | #[get("/<foo*>")] //~ ERROR `foo*` is not a valid identifier
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: parameter names must be valid identifiers
|
||||
|
||||
error: `!` is not a valid identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:84:9
|
||||
|
|
||||
84 | #[get("/<!>")] //~ ERROR `!` is not a valid identifier
|
||||
| ^^^
|
||||
|
|
||||
= help: parameter names must be valid identifiers
|
||||
|
||||
error: `name>:<id` is not a valid identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:88:9
|
||||
|
|
||||
88 | #[get("/<name>:<id>")] //~ ERROR `name>:<id` is not a valid identifier
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: parameter names must be valid identifiers
|
||||
|
||||
error: malformed parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:94:20
|
||||
|
|
||||
94 | #[get("/", data = "foo")] //~ ERROR malformed parameter
|
||||
| ^^^
|
||||
|
|
||||
= help: parameter must be of the form '<param>'
|
||||
|
||||
error: malformed parameter
|
||||
--> $DIR/route-path-bad-syntax.rs:98:20
|
||||
|
|
||||
98 | #[get("/", data = "<foo..>")] //~ ERROR malformed parameter
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: parameter must be of the form '<param>'
|
||||
|
||||
error: parameter is missing a closing bracket
|
||||
--> $DIR/route-path-bad-syntax.rs:102:20
|
||||
|
|
||||
102 | #[get("/", data = "<foo")] //~ ERROR missing a closing bracket
|
||||
| ^^^^
|
||||
|
|
||||
= help: did you mean '<foo>'?
|
||||
|
||||
error: `test ` is not a valid identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:106:20
|
||||
|
|
||||
106 | #[get("/", data = "<test >")] //~ ERROR `test ` is not a valid identifier
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: parameter names must be valid identifiers
|
||||
|
||||
error: parameters must be named
|
||||
--> $DIR/route-path-bad-syntax.rs:112:9
|
||||
|
|
||||
112 | #[get("/<_>")] //~ ERROR must be named
|
||||
| ^^^
|
||||
|
|
||||
= help: use a name such as `_guard` or `_param`
|
||||
|
||||
error: parameter names cannot be empty
|
||||
--> $DIR/route-path-bad-syntax.rs:117:9
|
||||
|
|
||||
117 | #[get("/<>")] //~ ERROR cannot be empty
|
||||
| ^^
|
||||
|
||||
error: malformed parameter or identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:120:9
|
||||
|
|
||||
120 | #[get("/<id><")] //~ ERROR malformed parameter
|
||||
| ^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
= help: identifiers cannot contain '<' or '>'
|
||||
|
||||
error: malformed parameter or identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:125:9
|
||||
|
|
||||
125 | #[get("/<<<<id><")] //~ ERROR malformed parameter
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
= help: identifiers cannot contain '<' or '>'
|
||||
|
||||
error: malformed parameter or identifier
|
||||
--> $DIR/route-path-bad-syntax.rs:130:9
|
||||
|
|
||||
130 | #[get("/<>name><")] //~ ERROR malformed parameter
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: parameters must be of the form '<param>'
|
||||
= help: identifiers cannot contain '<' or '>'
|
||||
|
||||
error: aborting due to 31 previous errors
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
struct Q;
|
||||
|
||||
#[get("/<foo>")]
|
||||
fn f0(foo: Q) {} //~ ERROR FromParam
|
||||
|
||||
#[get("/<foo..>")]
|
||||
fn f1(foo: Q) {} //~ ERROR FromSegments
|
||||
|
||||
#[get("/?<foo>")]
|
||||
fn f2(foo: Q) {} //~ ERROR FromFormValue
|
||||
|
||||
#[get("/?<foo..>")]
|
||||
fn f3(foo: Q) {} //~ ERROR FromQuery
|
||||
|
||||
#[post("/", data = "<foo>")]
|
||||
fn f4(foo: Q) {} //~ ERROR FromData
|
||||
|
||||
#[get("/<foo>")]
|
||||
fn f5(a: Q, foo: Q) {}
|
||||
//~^ ERROR FromParam
|
||||
//~^^ ERROR FromRequest
|
||||
|
||||
#[get("/<foo>/other/<bar>/<good>/okay")]
|
||||
fn f6(a: Q, foo: Q, good: usize, bar: Q) {}
|
||||
//~^ ERROR FromParam
|
||||
//~^^ ERROR FromParam
|
||||
//~^^^ ERROR FromRequest
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,65 @@
|
|||
error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:8:7
|
||||
|
|
||||
8 | fn f0(foo: Q) {} //~ ERROR FromParam
|
||||
| ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromSegments<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:11:7
|
||||
|
|
||||
11 | fn f1(foo: Q) {} //~ ERROR FromSegments
|
||||
| ^^^^^^ the trait `rocket::request::FromSegments<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromFormValue<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:14:7
|
||||
|
|
||||
14 | fn f2(foo: Q) {} //~ ERROR FromFormValue
|
||||
| ^^^^^^ the trait `rocket::request::FromFormValue<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromQuery<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:17:7
|
||||
|
|
||||
17 | fn f3(foo: Q) {} //~ ERROR FromQuery
|
||||
| ^^^^^^ the trait `rocket::request::FromQuery<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::data::FromDataSimple` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:20:7
|
||||
|
|
||||
20 | fn f4(foo: Q) {} //~ ERROR FromData
|
||||
| ^^^^^^ the trait `rocket::data::FromDataSimple` is not implemented for `Q`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `rocket::data::FromData<'_>` for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromRequest<'_, '_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:23:7
|
||||
|
|
||||
23 | fn f5(a: Q, foo: Q) {}
|
||||
| ^^^^ the trait `rocket::request::FromRequest<'_, '_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:23:13
|
||||
|
|
||||
23 | fn f5(a: Q, foo: Q) {}
|
||||
| ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromRequest<'_, '_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:28:7
|
||||
|
|
||||
28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {}
|
||||
| ^^^^ the trait `rocket::request::FromRequest<'_, '_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:28:13
|
||||
|
|
||||
28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {}
|
||||
| ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q`
|
||||
|
||||
error[E0277]: the trait bound `Q: rocket::request::FromParam<'_>` is not satisfied
|
||||
--> $DIR/route-type-errors.rs:28:34
|
||||
|
|
||||
28 | fn f6(a: Q, foo: Q, good: usize, bar: Q) {}
|
||||
| ^^^^^^ the trait `rocket::request::FromParam<'_>` is not implemented for `Q`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -0,0 +1,26 @@
|
|||
// must-compile-successfully
|
||||
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
// Check for unknown media types.
|
||||
|
||||
#[get("/", format = "application/x-custom")] //~ WARNING not a known media type
|
||||
fn f0() {}
|
||||
|
||||
#[get("/", format = "x-custom/plain")] //~ WARNING not a known media type
|
||||
fn f1() {}
|
||||
|
||||
#[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
||||
fn f2() {}
|
||||
|
||||
// Check if a data argument is used with a usually non-payload bearing method.
|
||||
|
||||
#[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
fn g0(_foo: rocket::Data) {}
|
||||
|
||||
#[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
fn g1(_foo: rocket::Data) {}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,42 @@
|
|||
warning: 'application/x-custom' is not a known media type
|
||||
--> $DIR/route-warnings.rs:9:21
|
||||
|
|
||||
9 | #[get("/", format = "application/x-custom")] //~ WARNING not a known media type
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: 'x-custom/plain' is not a known media type
|
||||
--> $DIR/route-warnings.rs:12:21
|
||||
|
|
||||
12 | #[get("/", format = "x-custom/plain")] //~ WARNING not a known media type
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: 'x-custom/x-custom' is not a known media type
|
||||
--> $DIR/route-warnings.rs:15:21
|
||||
|
|
||||
15 | #[get("/", format = "x-custom/x-custom")] //~ WARNING not a known media type
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
warning: `data` used with non-payload-supporting method
|
||||
--> $DIR/route-warnings.rs:20:12
|
||||
|
|
||||
20 | #[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: 'GET' does not typically support payloads
|
||||
--> $DIR/route-warnings.rs:20:3
|
||||
|
|
||||
20 | #[get("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
| ^^^
|
||||
|
||||
warning: `data` used with non-payload-supporting method
|
||||
--> $DIR/route-warnings.rs:23:13
|
||||
|
|
||||
23 | #[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: 'HEAD' does not typically support payloads
|
||||
--> $DIR/route-warnings.rs:23:3
|
||||
|
|
||||
23 | #[head("/", data = "<_foo>")] //~ WARNING used with non-payload-supporting method
|
||||
| ^^^^
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::http::RawStr;
|
||||
use rocket::request::FromParam;
|
||||
|
||||
struct S;
|
||||
|
||||
impl<'a> FromParam<'a> for S {
|
||||
type Error = ();
|
||||
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> { Ok(S) }
|
||||
}
|
||||
|
||||
#[post("/<id>")]
|
||||
fn simple(id: i32) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn not_uri_display(id: i32, name: S) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn not_uri_display_but_unused(id: i32, name: S) { }
|
||||
|
||||
fn main() {
|
||||
uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
|
||||
uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
|
||||
uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64>
|
||||
uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_>
|
||||
}
|
|
@ -1,31 +1,31 @@
|
|||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:27:23
|
||||
--> $DIR/typed-uri-bad-type.rs:25:23
|
||||
|
|
||||
27 | uri!(simple: id = "hi");
|
||||
25 | uri!(simple: id = "hi"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
|
||||
| ^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32`
|
||||
|
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<&str>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:28:18
|
||||
--> $DIR/typed-uri-bad-type.rs:26:18
|
||||
|
|
||||
28 | uri!(simple: "hello");
|
||||
26 | uri!(simple: "hello"); //~ ERROR i32: rocket::http::uri::FromUriParam<&str>
|
||||
| ^^^^^^^ the trait `rocket::http::uri::FromUriParam<&str>` is not implemented for `i32`
|
||||
|
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `i32: rocket::http::uri::FromUriParam<i64>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:29:23
|
||||
--> $DIR/typed-uri-bad-type.rs:27:23
|
||||
|
|
||||
29 | uri!(simple: id = 239239i64);
|
||||
27 | uri!(simple: id = 239239i64); //~ ERROR i32: rocket::http::uri::FromUriParam<i64>
|
||||
| ^^^^^^^^^ the trait `rocket::http::uri::FromUriParam<i64>` is not implemented for `i32`
|
||||
|
|
||||
= note: required by `rocket::http::uri::FromUriParam::from_uri_param`
|
||||
|
||||
error[E0277]: the trait bound `S: rocket::http::uri::FromUriParam<_>` is not satisfied
|
||||
--> $DIR/typed-uri-bad-type.rs:30:31
|
||||
--> $DIR/typed-uri-bad-type.rs:28:31
|
||||
|
|
||||
30 | uri!(not_uri_display: 10, S);
|
||||
28 | uri!(not_uri_display: 10, S); //~ ERROR S: rocket::http::uri::FromUriParam<_>
|
||||
| ^ the trait `rocket::http::uri::FromUriParam<_>` is not implemented for `S`
|
||||
|
||||
error: aborting due to 4 previous errors
|
|
@ -0,0 +1,72 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use rocket::http::Cookies;
|
||||
|
||||
#[post("/<id>")]
|
||||
fn has_one(id: i32) { }
|
||||
|
||||
#[post("/<id>")]
|
||||
fn has_one_guarded(cookies: Cookies, id: i32) { }
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn has_two(cookies: Cookies, id: i32, name: String) { }
|
||||
|
||||
fn main() {
|
||||
uri!(has_one); //~ ERROR expects 1 parameter but 0
|
||||
|
||||
uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2
|
||||
uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2
|
||||
uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2
|
||||
|
||||
uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3
|
||||
uri!(has_two: 10); //~ ERROR expects 2 parameters but 1
|
||||
|
||||
uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameter: `name`
|
||||
|
||||
uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameter: `name`
|
||||
|
||||
uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameters: `name`, `age`
|
||||
|
||||
uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameters: `name`, `age`
|
||||
//~^^ HELP duplicate parameter: `id`
|
||||
|
||||
uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
//~^ HELP duplicate parameter: `id`
|
||||
|
||||
uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
//~^ HELP duplicate parameter: `id`
|
||||
|
||||
uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameter: `name`
|
||||
//~^^ HELP missing parameter: `id`
|
||||
|
||||
uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameter: `cookies`
|
||||
|
||||
uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP unknown parameter: `cookies`
|
||||
|
||||
uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
//~^ HELP duplicate parameter: `id`
|
||||
//~^^ HELP missing parameter: `name`
|
||||
|
||||
uri!(has_two: name = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP missing parameter: `id`
|
||||
|
||||
uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
//~^ HELP duplicate parameter: `id`
|
||||
//~^^ HELP missing parameter: `name`
|
||||
//~^^^ HELP unknown parameter: `cookies`
|
||||
|
||||
uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
//~^ HELP missing parameter: `name`
|
||||
//~^^ HELP unknown parameter: `cookies`
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
error: `has_one` route uri expects 1 parameter but 0 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:19:10
|
||||
|
|
||||
19 | uri!(has_one); //~ ERROR expects 1 parameter but 0
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:21:19
|
||||
|
|
||||
21 | uri!(has_one: 1, 23); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:22:19
|
||||
|
|
||||
22 | uri!(has_one: "Hello", 23, ); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_one_guarded` route uri expects 1 parameter but 2 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:23:27
|
||||
|
|
||||
23 | uri!(has_one_guarded: "hi", 100); //~ ERROR expects 1 parameter but 2
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: expected parameter: id: i32
|
||||
|
||||
error: `has_two` route uri expects 2 parameters but 3 were supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:25:19
|
||||
|
|
||||
25 | uri!(has_two: 10, "hi", "there"); //~ ERROR expects 2 parameters but 3
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: expected parameters: id: i32, name: String
|
||||
|
||||
error: `has_two` route uri expects 2 parameters but 1 was supplied
|
||||
--> $DIR/typed-uris-bad-params.rs:26:19
|
||||
|
|
||||
26 | uri!(has_two: 10); //~ ERROR expects 2 parameters but 1
|
||||
| ^^
|
||||
|
|
||||
= note: expected parameters: id: i32, name: String
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:28:19
|
||||
|
|
||||
28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:28:29
|
||||
|
|
||||
28 | uri!(has_one: id = 100, name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:31:19
|
||||
|
|
||||
31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:31:19
|
||||
|
|
||||
31 | uri!(has_one: name = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
|
|
||||
34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameters: `name`, `age`
|
||||
--> $DIR/typed-uris-bad-params.rs:34:19
|
||||
|
|
||||
34 | uri!(has_one: name = 100, age = 50, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^ ^^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameters: `name`, `age`
|
||||
--> $DIR/typed-uris-bad-params.rs:37:19
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^^^ ^^^
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:37:51
|
||||
|
|
||||
37 | uri!(has_one: name = 100, age = 50, id = 100, id = 50); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:41:19
|
||||
|
|
||||
41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:41:29
|
||||
|
|
||||
41 | uri!(has_one: id = 100, id = 100); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:44:19
|
||||
|
|
||||
44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:44:29
|
||||
|
|
||||
44 | uri!(has_one: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_one` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:47:19
|
||||
|
|
||||
47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
= help: missing parameter: `id`
|
||||
help: unknown parameter: `name`
|
||||
--> $DIR/typed-uris-bad-params.rs:47:19
|
||||
|
|
||||
47 | uri!(has_one: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^
|
||||
|
||||
error: invalid parameters for `has_one_guarded` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:51:27
|
||||
|
|
||||
51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:51:27
|
||||
|
|
||||
51 | uri!(has_one_guarded: cookies = "hi", id = 100); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `has_one_guarded` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:54:27
|
||||
|
|
||||
54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:54:37
|
||||
|
|
||||
54 | uri!(has_one_guarded: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:57:19
|
||||
|
|
||||
57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:57:29
|
||||
|
|
||||
57 | uri!(has_two: id = 100, id = 100, ); //~ ERROR invalid parameters
|
||||
| ^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:61:19
|
||||
|
|
||||
61 | uri!(has_two: name = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `id`
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:64:19
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:64:19
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
help: duplicate parameter: `id`
|
||||
--> $DIR/typed-uris-bad-params.rs:64:45
|
||||
|
|
||||
64 | uri!(has_two: cookies = "hi", id = 100, id = 10, id = 10); //~ ERROR invalid parameters
|
||||
| ^^ ^^
|
||||
|
||||
error: invalid parameters for `has_two` route uri
|
||||
--> $DIR/typed-uris-bad-params.rs:69:19
|
||||
|
|
||||
69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: uri parameters are: id: i32, name: String
|
||||
= help: missing parameter: `name`
|
||||
help: unknown parameter: `cookies`
|
||||
--> $DIR/typed-uris-bad-params.rs:69:29
|
||||
|
|
||||
69 | uri!(has_two: id = 100, cookies = "hi"); //~ ERROR invalid parameters
|
||||
| ^^^^^^^
|
||||
|
||||
error: aborting due to 19 previous errors
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[post("/<id>/<name>")]
|
||||
fn simple(id: i32, name: String) -> &'static str { "" }
|
||||
|
||||
fn main() {
|
||||
uri!(simple: id = 100, "Hello"); //~ ERROR named and unnamed
|
||||
uri!(simple: "Hello", id = 100); //~ ERROR named and unnamed
|
||||
uri!(simple,); //~ ERROR expected `:`
|
||||
uri!(simple:); //~ ERROR argument list
|
||||
uri!("/mount"); //~ ERROR route path
|
||||
uri!("/mount",); //~ ERROR expected identifier
|
||||
uri!("mount", simple); //~ invalid mount point
|
||||
uri!("/mount/<id>", simple); //~ invalid mount point
|
||||
uri!(); //~ unexpected end of input
|
||||
uri!(simple: id = ); //~ expected expression
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
error: named and unnamed parameters cannot be mixed
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:9:18
|
||||
|
|
||||
9 | uri!(simple: id = 100, "Hello"); //~ ERROR named and unnamed
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named and unnamed parameters cannot be mixed
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:10:18
|
||||
|
|
||||
10 | uri!(simple: "Hello", id = 100); //~ ERROR named and unnamed
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: expected `:`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:11:16
|
||||
|
|
||||
11 | uri!(simple,); //~ ERROR expected `:`
|
||||
| ^
|
||||
|
||||
error: expected argument list after `:`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:12:16
|
||||
|
|
||||
12 | uri!(simple:); //~ ERROR argument list
|
||||
| ^
|
||||
|
||||
error: unexpected end of input: expected ',' followed by route path
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:13:10
|
||||
|
|
||||
13 | uri!("/mount"); //~ ERROR route path
|
||||
| ^^^^^^^^
|
||||
|
||||
error: unexpected end of input, expected identifier
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:14:5
|
||||
|
|
||||
14 | uri!("/mount",); //~ ERROR expected identifier
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: invalid mount point; mount points must be static, absolute URIs: `/example`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:15:10
|
||||
|
|
||||
15 | uri!("mount", simple); //~ invalid mount point
|
||||
| ^^^^^^^
|
||||
|
||||
error: invalid mount point; mount points must be static, absolute URIs: `/example`
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:16:10
|
||||
|
|
||||
16 | uri!("/mount/<id>", simple); //~ invalid mount point
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
error: unexpected end of input, call to `uri!` cannot be empty
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:17:5
|
||||
|
|
||||
17 | uri!(); //~ unexpected end of input
|
||||
| ^^^^^^^
|
||||
|
||||
error: unexpected end of input, expected expression
|
||||
--> $DIR/typed-uris-invalid-syntax.rs:18:5
|
||||
|
|
||||
18 | uri!(simple: id = ); //~ expected expression
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
|
|
@ -34,7 +34,7 @@ shift
|
|||
shopt -s nullglob
|
||||
|
||||
while [[ "$1" != "" ]]; do
|
||||
for EXT in "stderr" "stdout" "fixed"; do
|
||||
for EXT in "stderr" "fixed"; do
|
||||
for OUT_NAME in $BUILD_DIR/${1%.rs}.*$EXT; do
|
||||
OUT_DIR=`dirname "$1"`
|
||||
OUT_BASE=`basename "$OUT_NAME"`
|
||||
|
|
|
@ -35,4 +35,3 @@ optional = true
|
|||
|
||||
[dev-dependencies]
|
||||
rocket = { version = "0.4.0-dev", path = "../lib" }
|
||||
rocket_codegen = { version = "0.4.0-dev", path = "../codegen" }
|
||||
|
|
|
@ -40,7 +40,15 @@ impl Method {
|
|||
}
|
||||
|
||||
/// Returns `true` if an HTTP request with the method represented by `self`
|
||||
/// supports a payload.
|
||||
/// always supports a payload.
|
||||
///
|
||||
/// The following methods always support payloads:
|
||||
///
|
||||
/// * `PUT`, `POST`, `DELETE`, `PATCH`
|
||||
///
|
||||
/// The following methods _do not_ always support payloads:
|
||||
///
|
||||
/// * `GET`, `HEAD`, `CONNECT`, `TRACE`, `OPTIONS`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
|
|
@ -108,7 +108,8 @@ impl<'a> RouteSegment<'a> {
|
|||
return Ok(RouteSegment { string, source, name, kind, index });
|
||||
} else if segment.is_empty() {
|
||||
return Err(Empty);
|
||||
} else if segment.starts_with('<') && segment.len() > 1 {
|
||||
} else if segment.starts_with('<') && segment.len() > 1
|
||||
&& !segment[1..].contains('<') && !segment[1..].contains('>') {
|
||||
return Err(MissingClose);
|
||||
} else if segment.contains('>') || segment.contains('<') {
|
||||
return Err(Malformed);
|
||||
|
|
|
@ -105,8 +105,7 @@ use self::priv_encode_set::PATH_ENCODE_SET;
|
|||
/// dynamic parameter type.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![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;
|
||||
/// # fn main() { }
|
||||
/// use rocket::http::RawStr;
|
||||
|
@ -229,7 +228,9 @@ macro_rules! impl_with_display {
|
|||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
impl_with_display! {
|
||||
i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
|
||||
i8, i16, i32, i64, i128, isize,
|
||||
u8, u16, u32, u64, u128, usize,
|
||||
f32, f64, bool,
|
||||
IpAddr, Ipv4Addr, Ipv6Addr
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ isatty = "0.1"
|
|||
[dev-dependencies]
|
||||
# TODO: Find a way to not depend on this.
|
||||
lazy_static = "1.0"
|
||||
rocket_codegen = { version = "0.4.0-dev", path = "../codegen" }
|
||||
|
||||
[build-dependencies]
|
||||
yansi = "0.4"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#![feature(specialization)]
|
||||
#![feature(plugin, decl_macro)]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(try_trait)]
|
||||
#![feature(fnbox)]
|
||||
#![feature(never_type)]
|
||||
|
@ -30,12 +30,10 @@
|
|||
//!
|
||||
//! ## Libraries
|
||||
//!
|
||||
//! Rocket's functionality is split into three crates:
|
||||
//! Rocket's functionality is split into two crates:
|
||||
//!
|
||||
//! 1. [Core](/rocket) - The core library. Needed by every Rocket application.
|
||||
//! 2. [Codegen](/rocket_codegen) - Core code generation plugin. Should always
|
||||
//! be used alongside `rocket`, though it's not necessary.
|
||||
//! 3. [Contrib](/rocket_contrib) - Provides useful functionality for many
|
||||
//! 2. [Contrib](/rocket_contrib) - Provides useful functionality for many
|
||||
//! Rocket applications. Completely optional.
|
||||
//!
|
||||
//! ## Usage
|
||||
|
@ -48,7 +46,6 @@
|
|||
//! ```rust,ignore
|
||||
//! [dependencies]
|
||||
//! rocket = "*"
|
||||
//! rocket_codegen = "*"
|
||||
//! ```
|
||||
//!
|
||||
//! If you'll be deploying your project to [crates.io](https://crates.io),
|
||||
|
|
|
@ -5,13 +5,11 @@ use request::FormItems;
|
|||
///
|
||||
/// # Deriving
|
||||
///
|
||||
/// This trait can be automatically derived via the
|
||||
/// [rocket_codegen](/rocket_codegen) plugin. When deriving `FromForm`, every
|
||||
/// field in the structure must implement
|
||||
/// [FromFormValue](trait.FromFormValue.html). Rocket validates each field in
|
||||
/// the structure by calling its `FromFormValue` implementation. You may wish to
|
||||
/// implement `FromFormValue` for your own types for custom, automatic
|
||||
/// validation.
|
||||
/// This trait can be automatically derived. When deriving `FromForm`, every
|
||||
/// field in the structure must implement [`FromFormValue`]. Rocket validates
|
||||
/// each field in the structure by calling its `FromFormValue` implementation.
|
||||
/// You may wish to implement `FromFormValue` for your own types for custom,
|
||||
/// automatic validation.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
|
|
@ -428,11 +428,12 @@ impl<'r> Request<'r> {
|
|||
}).as_ref()
|
||||
}
|
||||
|
||||
/// Returns the media type "format" of the request if it is present.
|
||||
/// Returns the media type "format" of the request.
|
||||
///
|
||||
/// The "format" of a request is either the Content-Type, if the request
|
||||
/// methods indicates support for a payload, or the preferred media type in
|
||||
/// the Accept header otherwise.
|
||||
/// the Accept header otherwise. If the method indicates no payload and no
|
||||
/// Accept header is specified, a media type of `Any` is returned.
|
||||
///
|
||||
/// The media type returned from this method is used to match against the
|
||||
/// `format` route attribute.
|
||||
|
@ -455,13 +456,16 @@ impl<'r> Request<'r> {
|
|||
/// # });
|
||||
/// ```
|
||||
pub fn format(&self) -> Option<&MediaType> {
|
||||
static ANY: MediaType = MediaType::Any;
|
||||
if self.method().supports_payload() {
|
||||
self.content_type().map(|ct| ct.media_type())
|
||||
} else {
|
||||
// FIXME: Should we be using `accept_first` or `preferred`? Or
|
||||
// should we be checking neither and instead pass things through
|
||||
// where the client accepts the thing at all?
|
||||
self.accept().map(|accept| accept.preferred().media_type())
|
||||
self.accept()
|
||||
.map(|accept| accept.preferred().media_type())
|
||||
.or(Some(&ANY))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,7 @@ impl Route {
|
|||
self.method == other.method
|
||||
&& self.rank == other.rank
|
||||
&& paths_collide(self, other)
|
||||
&& match (self.format.as_ref(), other.format.as_ref()) {
|
||||
(Some(a), Some(b)) => media_types_collide(a, b),
|
||||
_ => true
|
||||
}
|
||||
&& formats_collide(self, other)
|
||||
}
|
||||
|
||||
/// Determines if this route matches against the given request. This means
|
||||
|
@ -43,13 +40,7 @@ impl Route {
|
|||
self.method == req.method()
|
||||
&& paths_match(self, req)
|
||||
&& queries_match(self, req)
|
||||
&& match self.format {
|
||||
Some(ref a) => match req.format() {
|
||||
Some(ref b) => media_types_collide(a, b),
|
||||
None => false
|
||||
},
|
||||
None => true
|
||||
}
|
||||
&& formats_match(self, req)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +107,40 @@ fn queries_match(route: &Route, request: &Request) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
fn formats_collide(route: &Route, other: &Route) -> bool {
|
||||
// When matching against the `Accept` header, the client can always provide
|
||||
// a media type that will cause a collision through non-specificity.
|
||||
if !route.method.supports_payload() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When matching against the `Content-Type` header, we'll only consider
|
||||
// requests as having a `Content-Type` if they're fully specified. If a
|
||||
// route doesn't have a `format`, it accepts all `Content-Type`s. If a
|
||||
// request doesn't have a format, it only matches routes without a format.
|
||||
match (route.format.as_ref(), other.format.as_ref()) {
|
||||
(Some(a), Some(b)) => media_types_collide(a, b),
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
fn formats_match(route: &Route, request: &Request) -> bool {
|
||||
if !route.method.supports_payload() {
|
||||
route.format.as_ref()
|
||||
.and_then(|a| request.format().map(|b| (a, b)))
|
||||
.map(|(a, b)| media_types_collide(a, b))
|
||||
.unwrap_or(true)
|
||||
} else {
|
||||
match route.format.as_ref() {
|
||||
Some(a) => match request.format() {
|
||||
Some(b) if b.specificity() == 2 => media_types_collide(a, b),
|
||||
_ => false
|
||||
}
|
||||
None => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn media_types_collide(first: &MediaType, other: &MediaType) -> bool {
|
||||
let collide = |a, b| a == "*" || b == "*" || a == b;
|
||||
collide(first.top(), other.top()) && collide(first.sub(), other.sub())
|
||||
|
@ -313,15 +338,15 @@ mod tests {
|
|||
assert!(!mt_mt_collide("something/*", "random/else"));
|
||||
}
|
||||
|
||||
fn r_mt_mt_collide<S1, S2>(m1: Method, mt1: S1, m2: Method, mt2: S2) -> bool
|
||||
fn r_mt_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool
|
||||
where S1: Into<Option<&'static str>>, S2: Into<Option<&'static str>>
|
||||
{
|
||||
let mut route_a = Route::new(m1, "/", dummy_handler);
|
||||
let mut route_a = Route::new(m, "/", dummy_handler);
|
||||
if let Some(mt_str) = mt1.into() {
|
||||
route_a.format = Some(mt_str.parse::<MediaType>().unwrap());
|
||||
}
|
||||
|
||||
let mut route_b = Route::new(m2, "/", dummy_handler);
|
||||
let mut route_b = Route::new(m, "/", dummy_handler);
|
||||
if let Some(mt_str) = mt2.into() {
|
||||
route_b.format = Some(mt_str.parse::<MediaType>().unwrap());
|
||||
}
|
||||
|
@ -331,23 +356,44 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_route_content_type_colliions() {
|
||||
assert!(r_mt_mt_collide(Get, "application/json", Get, "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", Get, "application/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", Get, "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, "any/thing", Get, "*/*"));
|
||||
// non-payload bearing routes always collide
|
||||
assert!(r_mt_mt_collide(Get, "application/json", "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", "application/json"));
|
||||
assert!(r_mt_mt_collide(Get, "*/json", "application/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, "any/thing", "*/*"));
|
||||
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "text/html"));
|
||||
assert!(r_mt_mt_collide(Get, None, Get, "*/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, "*/*", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", Get, None));
|
||||
assert!(r_mt_mt_collide(Get, None, "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, None, "text/html"));
|
||||
assert!(r_mt_mt_collide(Get, None, "*/*"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", None));
|
||||
assert!(r_mt_mt_collide(Get, "*/*", None));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", None));
|
||||
|
||||
assert!(!r_mt_mt_collide(Get, "text/html", Get, "application/*"));
|
||||
assert!(!r_mt_mt_collide(Get, "application/html", Get, "text/*"));
|
||||
assert!(!r_mt_mt_collide(Get, "*/json", Get, "text/html"));
|
||||
assert!(!r_mt_mt_collide(Get, "text/html", Get, "text/css"));
|
||||
assert!(r_mt_mt_collide(Get, "application/*", "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", "text/*"));
|
||||
assert!(r_mt_mt_collide(Get, "application/json", "text/html"));
|
||||
assert!(r_mt_mt_collide(Get, "text/html", "text/html"));
|
||||
|
||||
// payload bearing routes collide if the media types collide
|
||||
assert!(r_mt_mt_collide(Post, "application/json", "application/json"));
|
||||
assert!(r_mt_mt_collide(Post, "*/json", "application/json"));
|
||||
assert!(r_mt_mt_collide(Post, "*/json", "application/*"));
|
||||
assert!(r_mt_mt_collide(Post, "text/html", "text/*"));
|
||||
assert!(r_mt_mt_collide(Post, "any/thing", "*/*"));
|
||||
|
||||
assert!(r_mt_mt_collide(Post, None, "text/*"));
|
||||
assert!(r_mt_mt_collide(Post, None, "text/html"));
|
||||
assert!(r_mt_mt_collide(Post, None, "*/*"));
|
||||
assert!(r_mt_mt_collide(Post, "text/html", None));
|
||||
assert!(r_mt_mt_collide(Post, "*/*", None));
|
||||
assert!(r_mt_mt_collide(Post, "application/json", None));
|
||||
|
||||
assert!(!r_mt_mt_collide(Post, "text/html", "application/*"));
|
||||
assert!(!r_mt_mt_collide(Post, "application/html", "text/*"));
|
||||
assert!(!r_mt_mt_collide(Post, "*/json", "text/html"));
|
||||
assert!(!r_mt_mt_collide(Post, "text/html", "text/css"));
|
||||
assert!(!r_mt_mt_collide(Post, "other/html", "text/html"));
|
||||
}
|
||||
|
||||
fn req_route_mt_collide<S1, S2>(m: Method, mt1: S1, mt2: S2) -> bool
|
||||
|
@ -381,8 +427,9 @@ mod tests {
|
|||
assert!(req_route_mt_collide(Get, "application/json", "application/json"));
|
||||
assert!(req_route_mt_collide(Get, "text/html", "text/html"));
|
||||
assert!(req_route_mt_collide(Get, "text/html", "*/*"));
|
||||
assert!(req_route_mt_collide(Get, None, "text/html"));
|
||||
assert!(req_route_mt_collide(Get, None, "*/*"));
|
||||
assert!(req_route_mt_collide(Get, None, "text/*"));
|
||||
assert!(req_route_mt_collide(Get, None, "text/html"));
|
||||
assert!(req_route_mt_collide(Get, None, "application/json"));
|
||||
|
||||
assert!(req_route_mt_collide(Post, "text/html", None));
|
||||
|
@ -394,10 +441,19 @@ mod tests {
|
|||
assert!(req_route_mt_collide(Get, "application/json", None));
|
||||
assert!(req_route_mt_collide(Get, "x-custom/anything", None));
|
||||
assert!(req_route_mt_collide(Get, None, None));
|
||||
assert!(req_route_mt_collide(Get, None, "text/html"));
|
||||
assert!(req_route_mt_collide(Get, None, "application/json"));
|
||||
|
||||
assert!(req_route_mt_collide(Get, "text/html, text/plain", "text/html"));
|
||||
assert!(req_route_mt_collide(Get, "text/html; q=0.5, text/xml", "text/xml"));
|
||||
|
||||
assert!(!req_route_mt_collide(Post, None, "text/html"));
|
||||
assert!(!req_route_mt_collide(Post, None, "text/*"));
|
||||
assert!(!req_route_mt_collide(Post, None, "*/text"));
|
||||
assert!(!req_route_mt_collide(Post, None, "*/*"));
|
||||
assert!(!req_route_mt_collide(Post, None, "text/html"));
|
||||
assert!(!req_route_mt_collide(Post, None, "application/json"));
|
||||
|
||||
assert!(!req_route_mt_collide(Post, "application/json", "text/html"));
|
||||
assert!(!req_route_mt_collide(Post, "application/json", "text/*"));
|
||||
assert!(!req_route_mt_collide(Post, "application/json", "*/xml"));
|
||||
|
@ -406,7 +462,6 @@ mod tests {
|
|||
assert!(!req_route_mt_collide(Get, "application/json", "*/xml"));
|
||||
|
||||
assert!(!req_route_mt_collide(Post, None, "text/html"));
|
||||
assert!(!req_route_mt_collide(Post, None, "*/*"));
|
||||
assert!(!req_route_mt_collide(Post, None, "application/json"));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, decl_macro, proc_macro_non_items)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
|
|
|
@ -6,4 +6,3 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
rocket = { path = "../../core/lib" }
|
||||
rocket_codegen = { path = "../../core/codegen" }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
rocket = { path = "../../core/lib" }
|
||||
rocket_codegen = { path = "../../core/codegen" }
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
path = "../../contrib/lib"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(plugin, proc_macro_non_items, proc_macro_gen, decl_macro, never_type)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro, never_type)]
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
extern crate rocket_contrib;
|
||||
|
|
|
@ -6,7 +6,6 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
rocket = { path = "../../core/lib" }
|
||||
rocket_codegen = { path = "../../core/codegen" }
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -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;
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
|
|
@ -30,7 +30,7 @@ CONTRIB_ROOT=$(relative "contrib") || exit $?
|
|||
|
||||
# Root of project-like directories.
|
||||
CORE_LIB_ROOT=$(relative "core/lib") || exit $?
|
||||
CORE_CODEGEN_ROOT=$(relative "core/codegen") || exit $?
|
||||
# CORE_CODEGEN_ROOT=$(relative "core/codegen") || exit $?
|
||||
CORE_CODEGEN_NEXT_ROOT=$(relative "core/codegen_next") || exit $?
|
||||
CORE_HTTP_ROOT=$(relative "core/http") || exit $?
|
||||
CONTRIB_LIB_ROOT=$(relative "contrib/lib") || exit $?
|
||||
|
@ -41,7 +41,7 @@ DOC_DIR=$(relative "target/doc") || exit $?
|
|||
|
||||
ALL_PROJECT_DIRS=(
|
||||
"${CORE_LIB_ROOT}"
|
||||
"${CORE_CODEGEN_ROOT}"
|
||||
# "${CORE_CODEGEN_ROOT}"
|
||||
"${CORE_CODEGEN_NEXT_ROOT}"
|
||||
"${CORE_HTTP_ROOT}"
|
||||
"${CONTRIB_LIB_ROOT}"
|
||||
|
@ -53,7 +53,7 @@ if [ "${1}" = "-p" ]; then
|
|||
echo "CORE_ROOT: ${CORE_ROOT}"
|
||||
echo "CONTRIB_ROOT: ${CONTRIB_ROOT}"
|
||||
echo "CORE_LIB_ROOT: ${CORE_LIB_ROOT}"
|
||||
echo "CORE_CODEGEN_ROOT: ${CORE_CODEGEN_ROOT}"
|
||||
# echo "CORE_CODEGEN_ROOT: ${CORE_CODEGEN_ROOT}"
|
||||
echo "CORE_CODEGEN_NEXT_ROOT: ${CORE_CODEGEN_NEXT_ROOT}"
|
||||
echo "CORE_HTTP_ROOT: ${CORE_HTTP_ROOT}"
|
||||
echo "CONTRIB_LIB_ROOT: ${CONTRIB_LIB_ROOT}"
|
||||
|
|
|
@ -61,10 +61,9 @@ Modify `src/main.rs` so that it contains the code for the Rocket `Hello, world!`
|
|||
program, reproduced below:
|
||||
|
||||
```rust
|
||||
#![feature(plugin)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
extern crate rocket;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[get("/")]
|
||||
fn index() -> &'static str {
|
||||
|
|
|
@ -138,10 +138,9 @@ We typically call `launch` from the `main` function. Our complete _Hello,
|
|||
world!_ application thus looks like:
|
||||
|
||||
```rust
|
||||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
extern crate rocket;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[get("/world")]
|
||||
fn world() -> &'static str {
|
||||
|
@ -153,11 +152,11 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
Note that we've added the `#![feature(plugin, decl_macro)]` and
|
||||
`#![plugin(rocket_codegen)]` lines to tell Rust that we'll be using Rocket's
|
||||
code generation plugin. We've also imported the `rocket` crate into our
|
||||
namespace via `extern crate rocket`. Finally, we call the `launch` method in the
|
||||
`main` function.
|
||||
Note the `#![feature]` line: this tells Rust that we're opting in to compiler
|
||||
features vailable in the nightly release channel. This line must be in the crate
|
||||
root, typically `main.rs`. We've also imported the `rocket` crate and all of its
|
||||
macros into our namespace via `#[macro_use] extern crate rocket`. Finally, we
|
||||
call the `launch` method in the `main` function.
|
||||
|
||||
Running the application, the console shows:
|
||||
|
||||
|
|
|
@ -51,10 +51,9 @@ And finally, create a skeleton Rocket application to work off of in
|
|||
`src/main.rs`:
|
||||
|
||||
```rust
|
||||
#![feature(plugin, decl_macro)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
extern crate rocket;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
fn main() {
|
||||
rocket::ignite().launch();
|
||||
|
|
|
@ -44,10 +44,9 @@ margin = 9
|
|||
[[sections]]
|
||||
title = "Hello, Rocket!"
|
||||
code = '''
|
||||
#![feature(plugin)]
|
||||
#![plugin(rocket_codegen)]
|
||||
#![feature(proc_macro_non_items, proc_macro_gen, decl_macro)]
|
||||
|
||||
extern crate rocket;
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
#[get("/hello/<name>/<age>")]
|
||||
fn hello(name: String, age: u8) -> String {
|
||||
|
|
Loading…
Reference in New Issue