Skip to content

Commit 9806a9c

Browse files
committed
Merge branch 'master' into feature/mtls
2 parents 5d43b7f + f6e48fa commit 9806a9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+553
-129
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
# Version 0.3.15 (Jul 16, 2018)
2+
3+
## Codegen
4+
5+
* The `#[catch]` decorator and `catchers!` macro were introduced, replacing
6+
`#[error]` and `errors!`.
7+
* The `#[error]` decorator and `errors!` macro were deprecated.
8+
* Codegen was updated for `2018-07-15` nightly.
9+
* Minimum required `rustc` is `1.29.0-nightly 2018-07-15`.
10+
111
# Version 0.3.14 (Jun 22, 2018)
212

313
## Codegen

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ members = [
2222
"examples/content_types",
2323
"examples/ranking",
2424
"examples/testing",
25+
"examples/request_local_state",
2526
"examples/request_guard",
2627
"examples/stream",
2728
"examples/json",

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,4 @@ Rocket is licensed under either of the following, at your option:
149149
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
150150
* MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
151151

152-
The Rocket website source is licensed under [separate terms](site/README.md#license).
152+
The Rocket website source is licensed under [separate terms](site#license).

contrib/lib/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,8 @@ handlebars = { version = "0.32", optional = true }
3737
glob = { version = "^0.2", optional = true }
3838
tera = { version = "0.11", optional = true }
3939

40+
[dev-dependencies]
41+
rocket_codegen = { version = "0.4.0-dev", path = "../../core/codegen" }
42+
4043
[package.metadata.docs.rs]
4144
all-features = true

contrib/lib/src/json.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ use rocket::data::{self, Data, FromData};
77
use rocket::response::{self, Responder, content};
88
use rocket::http::Status;
99

10-
use serde::Serialize;
11-
use serde::de::DeserializeOwned;
12-
10+
use serde::{Serialize, Serializer};
11+
use serde::de::{Deserialize, DeserializeOwned, Deserializer};
1312
use serde_json;
1413

1514
pub use serde_json::error::Error as SerdeError;
@@ -186,6 +185,18 @@ impl<T> DerefMut for Json<T> {
186185
#[derive(Debug, Clone, PartialEq, Default)]
187186
pub struct JsonValue(pub serde_json::Value);
188187

188+
impl Serialize for JsonValue {
189+
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
190+
self.0.serialize(serializer)
191+
}
192+
}
193+
194+
impl<'de> Deserialize<'de> for JsonValue {
195+
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
196+
serde_json::Value::deserialize(deserializer).map(JsonValue)
197+
}
198+
}
199+
189200
impl JsonValue {
190201
#[inline(always)]
191202
fn into_inner(self) -> serde_json::Value {

contrib/lib/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub use msgpack::{MsgPack, MsgPackError};
7474
mod templates;
7575

7676
#[cfg(feature = "templates")]
77-
pub use templates::{Template, Engines};
77+
pub use templates::{Engines, Template, TemplateMetadata};
7878

7979
#[cfg(feature = "uuid")]
8080
mod uuid;

contrib/lib/src/msgpack.rs

+29-12
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,28 @@ pub use self::rmp_serde::decode::Error as MsgPackError;
2626
/// [Serde](https://github.com/serde-rs/serde). The data is parsed from the HTTP
2727
/// request body.
2828
///
29-
/// ```rust,ignore
30-
/// #[post("/users/", format = "application/msgpack", data = "<user>")]
29+
/// ```rust
30+
/// # #![feature(plugin, decl_macro)]
31+
/// # #![plugin(rocket_codegen)]
32+
/// # extern crate rocket;
33+
/// # extern crate rocket_contrib;
34+
/// # type User = usize;
35+
/// # fn main() { }
36+
/// #
37+
/// use rocket_contrib::MsgPack;
38+
///
39+
/// #[post("/users", format = "msgpack", data = "<user>")]
3140
/// fn new_user(user: MsgPack<User>) {
32-
/// ...
41+
/// /* ... */
3342
/// }
3443
/// ```
3544
///
36-
/// You don't _need_ to use `format = "application/msgpack"`, but it _may_ be
37-
/// what you want. Using `format = application/msgpack` means that any request
38-
/// that doesn't specify "application/msgpack" as its first `Content-Type:`
39-
/// header parameter will not be routed to this handler. By default, Rocket will
40-
/// accept a Content Type of any of the following for MessagePack data:
41-
/// `application/msgpack`, `application/x-msgpack`, `bin/msgpack`, or
42-
/// `bin/x-msgpack`.
45+
/// You don't _need_ to use `format = "msgpack"`, but it _may_ be what you want.
46+
/// Using `format = msgpack` means that any request that doesn't specify
47+
/// "application/msgpack" as its first `Content-Type:` header parameter will not
48+
/// be routed to this handler. By default, Rocket will accept a Content-Type of
49+
/// any of the following for MessagePack data: `application/msgpack`,
50+
/// `application/x-msgpack`, `bin/msgpack`, or `bin/x-msgpack`.
4351
///
4452
/// ## Sending MessagePack
4553
///
@@ -48,11 +56,20 @@ pub use self::rmp_serde::decode::Error as MsgPackError;
4856
/// [Serde](https://github.com/serde-rs/serde). The content type of the response
4957
/// is set to `application/msgpack` automatically.
5058
///
51-
/// ```rust,ignore
59+
/// ```rust
60+
/// # #![feature(plugin, decl_macro)]
61+
/// # #![plugin(rocket_codegen)]
62+
/// # extern crate rocket;
63+
/// # extern crate rocket_contrib;
64+
/// # type User = usize;
65+
/// # fn main() { }
66+
/// #
67+
/// use rocket_contrib::MsgPack;
68+
///
5269
/// #[get("/users/<id>")]
5370
/// fn user(id: usize) -> MsgPack<User> {
5471
/// let user_from_id = User::from(id);
55-
/// ...
72+
/// /* ... */
5673
/// MsgPack(user_from_id)
5774
/// }
5875
/// ```

contrib/lib/src/templates/metadata.rs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use rocket::{Request, State, Outcome};
2+
use rocket::http::Status;
3+
use rocket::request::{self, FromRequest};
4+
5+
use templates::Context;
6+
7+
/// The `TemplateMetadata` type: implements `FromRequest`, allowing dynamic
8+
/// queries about template metadata.
9+
///
10+
/// # Usage
11+
///
12+
/// First, ensure that the template [fairing](`rocket::fairing`) is attached to
13+
/// your Rocket application:
14+
///
15+
/// ```rust
16+
/// # extern crate rocket;
17+
/// # extern crate rocket_contrib;
18+
/// #
19+
/// use rocket_contrib::Template;
20+
///
21+
/// fn main() {
22+
/// rocket::ignite()
23+
/// .attach(Template::fairing())
24+
/// // ...
25+
/// # ;
26+
/// }
27+
/// ```
28+
///
29+
/// The `TemplateMetadata` type implements Rocket's `FromRequest` trait, so it can
30+
/// be used as a request guard in any request handler.
31+
///
32+
/// ```rust
33+
/// # #![feature(plugin, decl_macro)]
34+
/// # #![plugin(rocket_codegen)]
35+
/// # extern crate rocket;
36+
/// # #[macro_use] extern crate rocket_contrib;
37+
/// # fn main() { }
38+
/// #
39+
/// use rocket_contrib::{Template, TemplateMetadata};
40+
///
41+
/// #[get("/")]
42+
/// fn homepage(metadata: TemplateMetadata) -> Template {
43+
/// // Conditionally render a template if it's available.
44+
/// if metadata.contains_template("some-template") {
45+
/// Template::render("some-template", json!({ /* .. */ }))
46+
/// } else {
47+
/// Template::render("fallback", json!({ /* .. */ }))
48+
/// }
49+
/// }
50+
/// ```
51+
pub struct TemplateMetadata<'a>(&'a Context);
52+
53+
impl<'a> TemplateMetadata<'a> {
54+
/// Returns `true` if the template with name `name` was loaded at start-up
55+
/// time. Otherwise, returns `false`.
56+
///
57+
/// # Example
58+
///
59+
/// ```rust
60+
/// use rocket_contrib::TemplateMetadata;
61+
///
62+
/// fn handler(metadata: TemplateMetadata) {
63+
/// // Returns `true` if the template with name `"name"` was loaded.
64+
/// let loaded = metadata.contains_template("name");
65+
/// }
66+
/// ```
67+
pub fn contains_template(&self, name: &str) -> bool {
68+
self.0.templates.contains_key(name)
69+
}
70+
}
71+
72+
/// Retrieves the template metadata. If a template fairing hasn't been attached,
73+
/// an error is printed and an empty `Err` with status `InternalServerError`
74+
/// (`500`) is returned.
75+
impl<'a, 'r> FromRequest<'a, 'r> for TemplateMetadata<'a> {
76+
type Error = ();
77+
78+
fn from_request(request: &'a Request) -> request::Outcome<Self, ()> {
79+
request.guard::<State<Context>>()
80+
.succeeded()
81+
.and_then(|ctxt| Some(Outcome::Success(TemplateMetadata(ctxt.inner()))))
82+
.unwrap_or_else(|| {
83+
error_!("Uninitialized template context: missing fairing.");
84+
info_!("To use templates, you must attach `Template::fairing()`.");
85+
info_!("See the `Template` documentation for more information.");
86+
Outcome::Failure((Status::InternalServerError, ()))
87+
})
88+
}
89+
}

contrib/lib/src/templates/mod.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ extern crate glob;
66
#[cfg(feature = "handlebars_templates")] mod handlebars_templates;
77
mod engine;
88
mod context;
9+
mod metadata;
910

1011
pub use self::engine::Engines;
12+
pub use self::metadata::TemplateMetadata;
1113

1214
use self::engine::Engine;
1315
use self::context::Context;
@@ -287,15 +289,13 @@ impl Template {
287289
pub fn show<S, C>(rocket: &Rocket, name: S, context: C) -> Option<String>
288290
where S: Into<Cow<'static, str>>, C: Serialize
289291
{
290-
let ctxt = match rocket.state::<Context>() {
291-
Some(ctxt) => ctxt,
292-
None => {
293-
warn!("Uninitialized template context: missing fairing.");
294-
info!("To use templates, you must attach `Template::fairing()`.");
295-
info!("See the `Template` documentation for more information.");
296-
return None;
297-
}
298-
};
292+
let ctxt = rocket.state::<Context>().or_else(|| {
293+
warn!("Uninitialized template context: missing fairing.");
294+
info!("To use templates, you must attach `Template::fairing()`.");
295+
info!("See the `Template` documentation for more information.");
296+
None
297+
})?;
298+
299299
Template::render(name, context).finalize(&ctxt).ok().map(|v| v.0)
300300
}
301301

contrib/lib/tests/templates.rs

+50-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#![feature(plugin, decl_macro)]
2+
#![plugin(rocket_codegen)]
3+
14
extern crate rocket;
25
extern crate rocket_contrib;
36

@@ -6,9 +9,17 @@ mod templates_tests {
69
use std::env;
710
use std::path::PathBuf;
811

9-
use rocket::Rocket;
12+
use rocket::{Rocket, http::RawStr};
1013
use rocket::config::{Config, Environment};
11-
use rocket_contrib::Template;
14+
use rocket_contrib::{Template, TemplateMetadata};
15+
16+
#[get("/<engine>/<name>")]
17+
fn template_check(md: TemplateMetadata, engine: &RawStr, name: &RawStr) -> Option<()> {
18+
match md.contains_template(&format!("{}/{}", engine, name)) {
19+
true => Some(()),
20+
false => None
21+
}
22+
}
1223

1324
fn template_root() -> PathBuf {
1425
let cwd = env::current_dir().expect("current working directory");
@@ -20,13 +31,16 @@ mod templates_tests {
2031
.extra("template_dir", template_root().to_str().expect("template directory"))
2132
.expect("valid configuration");
2233

23-
::rocket::custom(config, true).attach(Template::fairing())
34+
::rocket::custom(config).attach(Template::fairing())
35+
.mount("/", routes![template_check])
2436
}
2537

2638
#[cfg(feature = "tera_templates")]
2739
mod tera_tests {
2840
use super::*;
2941
use std::collections::HashMap;
42+
use rocket::http::Status;
43+
use rocket::local::Client;
3044

3145
const UNESCAPED_EXPECTED: &'static str
3246
= "\nh_start\ntitle: _test_\nh_end\n\n\n<script />\n\nfoot\n";
@@ -48,12 +62,31 @@ mod templates_tests {
4862
let template = Template::show(&rocket, "tera/html_test", &map);
4963
assert_eq!(template, Some(ESCAPED_EXPECTED.into()));
5064
}
65+
66+
#[test]
67+
fn test_template_metadata_with_tera() {
68+
let client = Client::new(rocket()).unwrap();
69+
70+
let response = client.get("/tera/txt_test").dispatch();
71+
assert_eq!(response.status(), Status::Ok);
72+
73+
let response = client.get("/tera/html_test").dispatch();
74+
assert_eq!(response.status(), Status::Ok);
75+
76+
let response = client.get("/tera/not_existing").dispatch();
77+
assert_eq!(response.status(), Status::NotFound);
78+
79+
let response = client.get("/hbs/txt_test").dispatch();
80+
assert_eq!(response.status(), Status::NotFound);
81+
}
5182
}
5283

5384
#[cfg(feature = "handlebars_templates")]
5485
mod handlebars_tests {
5586
use super::*;
5687
use std::collections::HashMap;
88+
use rocket::http::Status;
89+
use rocket::local::Client;
5790

5891
const EXPECTED: &'static str
5992
= "Hello _test_!\n\n<main> &lt;script /&gt; hi </main>\nDone.\n\n";
@@ -69,5 +102,19 @@ mod templates_tests {
69102
let template = Template::show(&rocket, "hbs/test", &map);
70103
assert_eq!(template, Some(EXPECTED.into()));
71104
}
105+
106+
#[test]
107+
fn test_template_metadata_with_handlebars() {
108+
let client = Client::new(rocket()).unwrap();
109+
110+
let response = client.get("/hbs/test").dispatch();
111+
assert_eq!(response.status(), Status::Ok);
112+
113+
let response = client.get("/hbs/not_existing").dispatch();
114+
assert_eq!(response.status(), Status::NotFound);
115+
116+
let response = client.get("/tera/test").dispatch();
117+
assert_eq!(response.status(), Status::NotFound);
118+
}
72119
}
73120
}

core/codegen/build.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use yansi::Color::{Red, Yellow, Blue, White};
88
use version_check::{supports_features, is_min_version, is_min_date};
99

1010
// Specifies the minimum nightly version needed to compile Rocket's codegen.
11-
const MIN_DATE: &'static str = "2018-06-22";
12-
const MIN_VERSION: &'static str = "1.28.0-nightly";
11+
const MIN_DATE: &'static str = "2018-07-15";
12+
const MIN_VERSION: &'static str = "1.29.0-nightly";
1313

1414
fn main() {
1515
let ok_channel = supports_features();

core/codegen/src/decorators/derive_form.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use syntax_ext::deriving::generic::MethodDef;
1515
use syntax_ext::deriving::generic::{StaticStruct, Substructure, TraitDef, ty};
1616
use syntax_ext::deriving::generic::combine_substructure as c_s;
1717

18-
use utils::{strip_ty_lifetimes, is_valid_ident, SpanExt};
18+
use utils::{strip_ty_lifetimes, is_valid_ident, SpanExt, GenericParamExt};
1919

2020
static ONLY_STRUCTS_ERR: &'static str = "`FromForm` can only be derived for \
2121
structures with named fields.";
@@ -26,7 +26,7 @@ fn struct_lifetime(ecx: &mut ExtCtxt, item: &Annotatable, sp: Span) -> Option<St
2626
Annotatable::Item(ref item) => match item.node {
2727
ItemKind::Struct(_, ref generics) => {
2828
let mut lifetimes = generics.params.iter()
29-
.filter(|p| p.kind == GenericParamKind::Lifetime)
29+
.filter(|p| p.is_lifetime())
3030
.map(|p| p.ident.to_string());
3131

3232
let lifetime = lifetimes.next();

core/codegen/src/macros/uri.rs

+4
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ pub fn uri_internal(
128128
// Building <$T as ::rocket::http::uri::FromUriParam<_>>::from_uri_param($e).
129129
for (i, &(mut ident, ref ty)) in internal.fn_args.iter().enumerate() {
130130
let (span, mut expr) = (exprs[i].span, exprs[i].clone());
131+
132+
// Format argument names cannot begin with `_`, but a function parameter
133+
// might, so we prefix each parameter with the letters `fmt`.
134+
ident.name = Symbol::intern(&format!("fmt{}", ident.name));
131135
ident.span = span;
132136

133137
// path for call: <T as FromUriParam<_>>::from_uri_param

0 commit comments

Comments
 (0)