diff --git a/contrib/Cargo.toml b/contrib/Cargo.toml index 0c2b416a..c0af1bb0 100644 --- a/contrib/Cargo.toml +++ b/contrib/Cargo.toml @@ -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 } diff --git a/contrib/src/json/mod.rs b/contrib/src/json/mod.rs index 9cc42380..bd8568c7 100644 --- a/contrib/src/json/mod.rs +++ b/contrib/src/json/mod.rs @@ -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`, where `T` is @@ -122,42 +117,104 @@ impl DerefMut for JSON { } } -/// 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 { +/// 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`. +/// +/// ```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)+) }; } diff --git a/contrib/src/lib.rs b/contrib/src/lib.rs index 3a665595..18945897 100644 --- a/contrib/src/lib.rs +++ b/contrib/src/lib.rs @@ -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}; diff --git a/contrib/src/templates/mod.rs b/contrib/src/templates/mod.rs index 1dfc32f0..d8b4d209 100644 --- a/contrib/src/templates/mod.rs +++ b/contrib/src/templates/mod.rs @@ -142,6 +142,7 @@ impl Template { /// /// # context.insert("test", "test"); /// let template = Template::render("index", &context); + /// # assert_eq!(template.to_string(), ""); /// ``` pub fn render(name: S, context: &T) -> Template where S: AsRef, T: Serialize diff --git a/examples/content_types/Cargo.toml b/examples/content_types/Cargo.toml index 285dce08..c31e3cee 100644 --- a/examples/content_types/Cargo.toml +++ b/examples/content_types/Cargo.toml @@ -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"] } diff --git a/examples/handlebars_templates/Cargo.toml b/examples/handlebars_templates/Cargo.toml index 5947743f..c2e45575 100644 --- a/examples/handlebars_templates/Cargo.toml +++ b/examples/handlebars_templates/Cargo.toml @@ -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" diff --git a/examples/json/Cargo.toml b/examples/json/Cargo.toml index ac18b6e1..310b344f 100644 --- a/examples/json/Cargo.toml +++ b/examples/json/Cargo.toml @@ -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] diff --git a/examples/json/src/main.rs b/examples/json/src/main.rs index 52351097..50de2eb0 100644 --- a/examples/json/src/main.rs +++ b/examples/json/src/main.rs @@ -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> = Mutex::new(HashMap::new()); @@ -32,25 +29,25 @@ struct Message { // TODO: This example can be improved by using `route` with muliple HTTP verbs. #[post("/", format = "application/json", data = "")] -fn new(id: ID, message: JSON) -> JSON { +fn new(id: ID, message: JSON) -> JSON { 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("/", format = "application/json", data = "")] -fn update(id: ID, message: JSON) -> Option> { +fn update(id: ID, message: JSON) -> Option> { 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> { } #[error(404)] -fn not_found() -> JSON { - JSON(map! { - "status" => "error", - "reason" => "Resource was not found." - }) +fn not_found() -> JSON { + JSON(json!({ + "status": "error", + "reason": "Resource was not found." + })) } fn main() { diff --git a/examples/todo/Cargo.toml b/examples/todo/Cargo.toml index 6e66831b..349cb06b 100644 --- a/examples/todo/Cargo.toml +++ b/examples/todo/Cargo.toml @@ -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"] }