Update to serde 0.9, handlebars 0.25. Move from `map!` to `json!` macro.

Resolves #154.
This commit is contained in:
Sergio Benitez 2017-01-31 17:15:42 -08:00
parent 52d627cf44
commit 5fabb43a1b
9 changed files with 139 additions and 82 deletions

View File

@ -28,11 +28,11 @@ log = "^0.3"
uuid = { version = "^0.3", optional = true } uuid = { version = "^0.3", optional = true }
# JSON and templating dependencies. # JSON and templating dependencies.
serde = { version = "^0.8", optional = true } serde = { version = "^0.9", optional = true }
serde_json = { version = "^0.8", optional = true } serde_json = { version = "^0.9.3", optional = true }
# Templating dependencies only. # Templating dependencies only.
handlebars = { version = "^0.24", optional = true, features = ["serde_type"] } handlebars = { version = "^0.25", optional = true, features = ["serde_type"] }
glob = { version = "^0.2", optional = true } glob = { version = "^0.2", optional = true }
lazy_static = { version = "^0.2", optional = true } lazy_static = { version = "^0.2", optional = true }
# tera = { version = "^0.6", optional = true } # tera = { version = "^0.6", optional = true }

View File

@ -1,6 +1,3 @@
extern crate serde;
extern crate serde_json;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::io::Read; use std::io::Read;
@ -10,17 +7,15 @@ use rocket::data::{self, Data, FromData};
use rocket::response::{self, Responder, content}; use rocket::response::{self, Responder, content};
use rocket::http::Status; use rocket::http::Status;
use self::serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
pub use self::serde_json::error::Error as SerdeError; use serde_json;
pub use self::serde_json::value::Value; pub use serde_json::Value;
pub use serde_json::error::Error as SerdeError;
#[doc(hidden)] /// The JSON type: implements `FromData` and `Responder`, allowing you to easily
pub use self::serde_json::to_value; /// consume and respond with JSON.
/// The JSON type, which implements `FromData` and `Responder`. This type allows
/// you to trivially consume and respond with JSON in your Rocket application.
/// ///
/// If you're receiving JSON data, simple add a `data` parameter to your route /// If you're receiving JSON data, simple add a `data` parameter to your route
/// arguments and ensure the type o the parameter is a `JSON<T>`, where `T` is /// arguments and ensure the type o the parameter is a `JSON<T>`, where `T` is
@ -122,42 +117,104 @@ impl<T> DerefMut for JSON<T> {
} }
} }
/// A nice little macro to create JSON serializable HashMaps, convenient for /// A macro to create ad-hoc JSON serializable values using JSON syntax.
/// returning ad-hoc JSON messages.
/// ///
/// Keys can be any type that implements `Serialize`. All keys must have the /// # Usage
/// same type, which is usually an `&'static str`. Values can be any type that ///
/// implements `Serialize` as well, but each value is allowed to be a different /// To import the macro, add the `#[macro_use]` attribute to the `extern crate
/// type. /// rocket_contrib` invocation:
///
/// ```rust,ignore
/// #[macro_use] extern crate rocket_contrib;
/// ```
///
/// The return type of a macro invocation is
/// [Value](/rocket_contrib/enum.Value.html). A value created with this macro
/// can be returned from a handler as follows:
///
/// ```rust,ignore
/// use rocket_contrib::{JSON, Value};
///
/// #[get("/json")]
/// fn get_json() -> JSON<Value> {
/// JSON(json!({
/// "key": "value",
/// "array": [1, 2, 3, 4]
/// }))
/// }
/// ```
/// ///
/// # Examples /// # Examples
/// ///
/// ``` /// Create a simple JSON object with two keys: `"username"` and `"id"`:
///
/// ```rust
/// # #![allow(unused_variables)]
/// # #[macro_use] extern crate rocket_contrib; /// # #[macro_use] extern crate rocket_contrib;
/// # fn main() { /// # fn main() {
/// let map = map! { /// let value = json!({
/// "message" => "Done!", /// "username": "mjordan",
/// "success" => true, /// "id": 23
/// "count" => 3, /// });
/// }; /// # }
/// ```
/// ///
/// assert_eq!(map.len(), 3); /// Create a more complex object with a nested object and array:
/// assert_eq!(map.get("message").and_then(|v| v.as_str()), Some("Done!")); ///
/// assert_eq!(map.get("success").and_then(|v| v.as_bool()), Some(true)); /// ```rust
/// assert_eq!(map.get("count").and_then(|v| v.as_u64()), Some(3)); /// # #![allow(unused_variables)]
/// # #[macro_use] extern crate rocket_contrib;
/// # fn main() {
/// let value = json!({
/// "code": 200,
/// "success": true,
/// "payload": {
/// "features": ["serde", "json"],
/// "ids": [12, 121],
/// },
/// });
/// # }
/// ```
///
/// Variables or expressions can be interpolated into the JSON literal. Any type
/// interpolated into an array element or object value must implement Serde's
/// `Serialize` trait, while any type interpolated into a object key must
/// implement `Into<String>`.
///
/// ```rust
/// # #![allow(unused_variables)]
/// # #[macro_use] extern crate rocket_contrib;
/// # fn main() {
/// let code = 200;
/// let features = vec!["serde", "json"];
///
/// let value = json!({
/// "code": code,
/// "success": code == 200,
/// "payload": {
/// features[0]: features[1]
/// }
/// });
/// # }
/// ```
///
/// Trailing commas are allowed inside both arrays and objects.
///
/// ```rust
/// # #![allow(unused_variables)]
/// # #[macro_use] extern crate rocket_contrib;
/// # fn main() {
/// let value = json!([
/// "notice",
/// "the",
/// "trailing",
/// "comma -->",
/// ]);
/// # } /// # }
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! map { macro_rules! json {
($($key:expr => $value:expr),+) => ({ ($($json:tt)+) => {
use ::std::collections::HashMap; json_internal!($($json)+)
use $crate::json::{Value, to_value};
let mut map: HashMap<_, Value> = HashMap::new();
$(map.insert($key, to_value($value));)+
map
});
($($key:expr => $value:expr),+,) => {
map!($($key => $value),+)
}; };
} }

View File

@ -1,4 +1,4 @@
#![feature(drop_types_in_const)] #![feature(drop_types_in_const, macro_reexport)]
//! This crate contains officially sanctioned contributor libraries that provide //! This crate contains officially sanctioned contributor libraries that provide
//! functionality commonly used by Rocket applications. //! functionality commonly used by Rocket applications.
@ -40,27 +40,29 @@
#[cfg(feature = "lazy_static_macro")] #[cfg(feature = "lazy_static_macro")]
extern crate lazy_static; extern crate lazy_static;
#[cfg_attr(feature = "json", macro_use)] #[cfg(feature = "serde")]
extern crate serde;
#[cfg(feature = "json")] #[cfg(feature = "json")]
#[cfg_attr(feature = "json", macro_reexport(json_internal))]
extern crate serde_json;
#[cfg(feature = "json")]
#[cfg_attr(feature = "json", macro_use)]
#[doc(hidden)] #[doc(hidden)]
pub mod json; pub mod json;
#[cfg(feature = "json")]
pub use json::{JSON, SerdeError, Value};
#[cfg(feature = "templates")] #[cfg(feature = "templates")]
mod templates; mod templates;
#[cfg(feature = "uuid")]
mod uuid;
#[cfg(feature = "json")]
pub use json::JSON;
#[cfg(feature = "json")]
pub use json::Value;
#[cfg(feature = "json")]
pub use json::SerdeError;
#[cfg(feature = "templates")] #[cfg(feature = "templates")]
pub use templates::Template; pub use templates::Template;
#[cfg(feature = "uuid")]
mod uuid;
#[cfg(feature = "uuid")] #[cfg(feature = "uuid")]
pub use uuid::{UUID, UuidParseError}; pub use uuid::{UUID, UuidParseError};

View File

@ -142,6 +142,7 @@ impl Template {
/// ///
/// # context.insert("test", "test"); /// # context.insert("test", "test");
/// let template = Template::render("index", &context); /// let template = Template::render("index", &context);
/// # assert_eq!(template.to_string(), "");
/// ``` /// ```
pub fn render<S, T>(name: S, context: &T) -> Template pub fn render<S, T>(name: S, context: &T) -> Template
where S: AsRef<str>, T: Serialize where S: AsRef<str>, T: Serialize

View File

@ -6,9 +6,9 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
serde = "0.8" serde = "0.9"
serde_json = "0.8" serde_json = "0.9"
serde_derive = "0.8" serde_derive = "0.9"
[dev-dependencies] [dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] } rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,9 +6,9 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
serde = "0.8" serde = "0.9"
serde_derive = "0.8" serde_derive = "0.9"
serde_json = "0.8" serde_json = "0.9"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]
path = "../../contrib" path = "../../contrib"

View File

@ -6,9 +6,9 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
serde = "0.8" serde = "0.9"
serde_json = "0.8" serde_json = "0.9"
serde_derive = "0.8" serde_derive = "0.9"
lazy_static = "*" lazy_static = "*"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]

View File

@ -16,9 +16,6 @@ use std::sync::Mutex;
// The type to represent the ID of a message. // The type to represent the ID of a message.
type ID = usize; type ID = usize;
// The type of a `map!` invocation.
type SimpleMap = HashMap<&'static str, Value>;
// We're going to store all of the messages here. No need for a DB. // We're going to store all of the messages here. No need for a DB.
lazy_static! { lazy_static! {
static ref MAP: Mutex<HashMap<ID, String>> = Mutex::new(HashMap::new()); static ref MAP: Mutex<HashMap<ID, String>> = Mutex::new(HashMap::new());
@ -32,25 +29,25 @@ struct Message {
// TODO: This example can be improved by using `route` with muliple HTTP verbs. // TODO: This example can be improved by using `route` with muliple HTTP verbs.
#[post("/<id>", format = "application/json", data = "<message>")] #[post("/<id>", format = "application/json", data = "<message>")]
fn new(id: ID, message: JSON<Message>) -> JSON<SimpleMap> { fn new(id: ID, message: JSON<Message>) -> JSON<Value> {
let mut hashmap = MAP.lock().expect("map lock."); let mut hashmap = MAP.lock().expect("map lock.");
if hashmap.contains_key(&id) { if hashmap.contains_key(&id) {
JSON(map!{ JSON(json!({
"status" => "error", "status": "error",
"reason" => "ID exists. Try put." "reason": "ID exists. Try put."
}) }))
} else { } else {
hashmap.insert(id, message.0.contents); hashmap.insert(id, message.0.contents);
JSON(map!{ "status" => "ok" }) JSON(json!({ "status": "ok" }))
} }
} }
#[put("/<id>", format = "application/json", data = "<message>")] #[put("/<id>", format = "application/json", data = "<message>")]
fn update(id: ID, message: JSON<Message>) -> Option<JSON<SimpleMap>> { fn update(id: ID, message: JSON<Message>) -> Option<JSON<Value>> {
let mut hashmap = MAP.lock().unwrap(); let mut hashmap = MAP.lock().unwrap();
if hashmap.contains_key(&id) { if hashmap.contains_key(&id) {
hashmap.insert(id, message.0.contents); hashmap.insert(id, message.0.contents);
Some(JSON(map!{ "status" => "ok" })) Some(JSON(json!({ "status": "ok" })))
} else { } else {
None None
} }
@ -68,11 +65,11 @@ fn get(id: ID) -> Option<JSON<Message>> {
} }
#[error(404)] #[error(404)]
fn not_found() -> JSON<SimpleMap> { fn not_found() -> JSON<Value> {
JSON(map! { JSON(json!({
"status" => "error", "status": "error",
"reason" => "Resource was not found." "reason": "Resource was not found."
}) }))
} }
fn main() { fn main() {

View File

@ -6,9 +6,9 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
serde = "0.8" serde = "0.9"
serde_json = "0.8" serde_json = "0.9"
serde_derive = "0.8" serde_derive = "0.9"
diesel = { version = "0.9", features = ["sqlite"] } diesel = { version = "0.9", features = ["sqlite"] }
diesel_codegen = { version = "0.9", features = ["sqlite"] } diesel_codegen = { version = "0.9", features = ["sqlite"] }