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 }
# JSON and templating dependencies.
serde = { version = "^0.8", optional = true }
serde_json = { version = "^0.8", optional = true }
serde = { version = "^0.9", optional = true }
serde_json = { version = "^0.9.3", optional = true }
# 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 }
lazy_static = { version = "^0.2", 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::io::Read;
@ -10,17 +7,15 @@ use rocket::data::{self, Data, FromData};
use rocket::response::{self, Responder, content};
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)]
pub use self::serde_json::to_value;
/// The JSON type, which implements `FromData` and `Responder`. This type allows
/// you to trivially consume and respond with JSON in your Rocket application.
/// The JSON type: implements `FromData` and `Responder`, allowing you to easily
/// consume and respond with JSON.
///
/// 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
@ -122,42 +117,104 @@ impl<T> DerefMut for JSON<T> {
}
}
/// A nice little macro to create JSON serializable HashMaps, convenient for
/// returning ad-hoc JSON messages.
/// A macro to create ad-hoc JSON serializable values using JSON syntax.
///
/// Keys can be any type that implements `Serialize`. All keys must have the
/// 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
/// type.
/// # Usage
///
/// To import the macro, add the `#[macro_use]` attribute to the `extern crate
/// 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
///
/// ```
/// Create a simple JSON object with two keys: `"username"` and `"id"`:
///
/// ```rust
/// # #![allow(unused_variables)]
/// # #[macro_use] extern crate rocket_contrib;
/// # fn main() {
/// let map = map! {
/// "message" => "Done!",
/// "success" => true,
/// "count" => 3,
/// };
/// let value = json!({
/// "username": "mjordan",
/// "id": 23
/// });
/// # }
/// ```
///
/// assert_eq!(map.len(), 3);
/// 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));
/// assert_eq!(map.get("count").and_then(|v| v.as_u64()), Some(3));
/// Create a more complex object with a nested object and array:
///
/// ```rust
/// # #![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_rules! map {
($($key:expr => $value:expr),+) => ({
use ::std::collections::HashMap;
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),+)
macro_rules! json {
($($json:tt)+) => {
json_internal!($($json)+)
};
}

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
//! functionality commonly used by Rocket applications.
@ -40,27 +40,29 @@
#[cfg(feature = "lazy_static_macro")]
extern crate lazy_static;
#[cfg_attr(feature = "json", macro_use)]
#[cfg(feature = "serde")]
extern crate serde;
#[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)]
pub mod json;
#[cfg(feature = "json")]
pub use json::{JSON, SerdeError, Value};
#[cfg(feature = "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")]
pub use templates::Template;
#[cfg(feature = "uuid")]
mod uuid;
#[cfg(feature = "uuid")]
pub use uuid::{UUID, UuidParseError};

View File

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

View File

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

View File

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

View File

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

View File

@ -16,9 +16,6 @@ use std::sync::Mutex;
// The type to represent the ID of a message.
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.
lazy_static! {
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.
#[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.");
if hashmap.contains_key(&id) {
JSON(map!{
"status" => "error",
"reason" => "ID exists. Try put."
})
JSON(json!({
"status": "error",
"reason": "ID exists. Try put."
}))
} else {
hashmap.insert(id, message.0.contents);
JSON(map!{ "status" => "ok" })
JSON(json!({ "status": "ok" }))
}
}
#[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();
if hashmap.contains_key(&id) {
hashmap.insert(id, message.0.contents);
Some(JSON(map!{ "status" => "ok" }))
Some(JSON(json!({ "status": "ok" })))
} else {
None
}
@ -68,11 +65,11 @@ fn get(id: ID) -> Option<JSON<Message>> {
}
#[error(404)]
fn not_found() -> JSON<SimpleMap> {
JSON(map! {
"status" => "error",
"reason" => "Resource was not found."
})
fn not_found() -> JSON<Value> {
JSON(json!({
"status": "error",
"reason": "Resource was not found."
}))
}
fn main() {

View File

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