Skip to content

Commit b99d38d

Browse files
authored
Better macros (#175)
* Improved macros * Restoring the logo * Fix the link to logo in docs * Version bump
1 parent 91c6da6 commit b99d38d

11 files changed

+257
-170
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "json"
3-
version = "0.12.2"
3+
version = "0.12.3"
44
authors = ["Maciej Hirsz <[email protected]>"]
55
description = "JSON implementation in Rust"
66
repository = "https://github.com/maciejhirsz/json-rust"

README.md

+11-13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
# json-rust
1+
![](https://raw.githubusercontent.com/maciejhirsz/json-rust/master/json-rust-logo-small.png)
22

3-
[![Travis shield](https://travis-ci.org/maciejhirsz/json-rust.svg)](https://travis-ci.org/maciejhirsz/json-rust)
4-
[![Crates.io version shield](https://img.shields.io/crates/v/json.svg)](https://crates.io/crates/json)
5-
[![Crates.io license shield](https://img.shields.io/crates/l/json.svg)](https://crates.io/crates/json)
3+
# json-rust
64

75
Parse and serialize [JSON](http://json.org/) with ease.
86

@@ -11,7 +9,6 @@ Parse and serialize [JSON](http://json.org/) with ease.
119
**[Cargo](https://crates.io/crates/json) -**
1210
**[Repository](https://github.com/maciejhirsz/json-rust)**
1311

14-
1512
## Why?
1613

1714
JSON is a very loose format where anything goes - arrays can hold mixed
@@ -39,10 +36,11 @@ let parsed = json::parse(r#"
3936
"#).unwrap();
4037

4138
let instantiated = object!{
42-
"code" => 200,
43-
"success" => true,
44-
"payload" => object!{
45-
"features" => array![
39+
// quotes on keys are optional
40+
"code": 200,
41+
success: true,
42+
payload: {
43+
features: [
4644
"awesome",
4745
"easyAPI",
4846
"lowLearningCurve"
@@ -59,10 +57,10 @@ Using macros and indexing, it's easy to work with the data.
5957

6058
```rust
6159
let mut data = object!{
62-
"foo" => false,
63-
"bar" => json::Null,
64-
"answer" => 42,
65-
"list" => array![json::Null, "world", true]
60+
foo: false,
61+
bar: null,
62+
answer: 42,
63+
list: [null, "world", true]
6664
};
6765

6866
// Partial equality is implemented for most raw types:

json-rust-logo-small.png

51.9 KB
Loading

src/lib.rs

+125-36
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! ![](http://terhix.com/doc/json-rust-logo-small.png)
1+
//! ![](https://raw.githubusercontent.com/maciejhirsz/json-rust/master/json-rust-logo-small.png)
22
//!
33
//! # json-rust
44
//!
@@ -38,10 +38,11 @@
3838
//! "#).unwrap();
3939
//!
4040
//! let instantiated = object!{
41-
//! "code" => 200,
42-
//! "success" => true,
43-
//! "payload" => object!{
44-
//! "features" => array![
41+
//! // quotes on keys are optional
42+
//! "code": 200,
43+
//! success: true,
44+
//! payload: {
45+
//! features: [
4546
//! "awesome",
4647
//! "easyAPI",
4748
//! "lowLearningCurve"
@@ -61,10 +62,10 @@
6162
//! # #[macro_use] extern crate json;
6263
//! # fn main() {
6364
//! let mut data = object!{
64-
//! "foo" => false,
65-
//! "bar" => json::Null,
66-
//! "answer" => 42,
67-
//! "list" => array![json::Null, "world", true]
65+
//! foo: false,
66+
//! bar: null,
67+
//! answer: 42,
68+
//! list: [null, "world", true]
6869
//! };
6970
//!
7071
//! // Partial equality is implemented for most raw types:
@@ -174,7 +175,7 @@
174175
//! ```
175176
//! # #[macro_use] extern crate json;
176177
//! # fn main() {
177-
//! let data = array!["foo", "bar", 100, true, json::Null];
178+
//! let data = array!["foo", "bar", 100, true, null];
178179
//! assert_eq!(data.dump(), r#"["foo","bar",100,true,null]"#);
179180
//! # }
180181
//! ```
@@ -185,9 +186,9 @@
185186
//! # #[macro_use] extern crate json;
186187
//! # fn main() {
187188
//! let data = object!{
188-
//! "name" => "John Doe",
189-
//! "age" => 30,
190-
//! "canJSON" => true
189+
//! name: "John Doe",
190+
//! age: 30,
191+
//! canJSON: true
191192
//! };
192193
//! assert_eq!(
193194
//! data.dump(),
@@ -279,16 +280,65 @@ pub fn stringify_pretty<T>(root: T, spaces: u16) -> String where T: Into<JsonVal
279280
macro_rules! array {
280281
[] => ($crate::JsonValue::new_array());
281282

282-
[ $( $item:expr ),* ] => ({
283-
let size = 0 $( + {let _ = $item; 1} )*;
283+
// Handles for token tree items
284+
[@ITEM($( $i:expr, )*) $item:tt, $( $cont:tt )+] => {
285+
$crate::array!(
286+
@ITEM($( $i, )* $crate::value!($item), )
287+
$( $cont )*
288+
)
289+
};
290+
(@ITEM($( $i:expr, )*) $item:tt,) => ({
291+
$crate::array!(@END $( $i, )* $crate::value!($item), )
292+
});
293+
(@ITEM($( $i:expr, )*) $item:tt) => ({
294+
$crate::array!(@END $( $i, )* $crate::value!($item), )
295+
});
296+
297+
// Handles for expression items
298+
[@ITEM($( $i:expr, )*) $item:expr, $( $cont:tt )+] => {
299+
$crate::array!(
300+
@ITEM($( $i, )* $crate::value!($item), )
301+
$( $cont )*
302+
)
303+
};
304+
(@ITEM($( $i:expr, )*) $item:expr,) => ({
305+
$crate::array!(@END $( $i, )* $crate::value!($item), )
306+
});
307+
(@ITEM($( $i:expr, )*) $item:expr) => ({
308+
$crate::array!(@END $( $i, )* $crate::value!($item), )
309+
});
310+
311+
// Construct the actual array
312+
(@END $( $i:expr, )*) => ({
313+
let size = 0 $( + {let _ = || $i; 1} )*;
284314
let mut array = Vec::with_capacity(size);
285315

286316
$(
287-
array.push($item.into());
317+
array.push($i.into());
288318
)*
289319

290320
$crate::JsonValue::Array(array)
291-
})
321+
});
322+
323+
// Entry point to the macro
324+
($( $cont:tt )+) => {
325+
$crate::array!(@ITEM() $($cont)*)
326+
};
327+
}
328+
329+
#[macro_export]
330+
/// Helper crate for converting types into `JsonValue`. It's used
331+
/// internally by the `object!` and `array!` macros.
332+
macro_rules! value {
333+
( null ) => { $crate::Null };
334+
( [$( $token:tt )*] ) => {
335+
// 10
336+
$crate::array![ $( $token )* ]
337+
};
338+
( {$( $token:tt )*} ) => {
339+
$crate::object!{ $( $token )* }
340+
};
341+
{ $value:expr } => { $value };
292342
}
293343

294344
/// Helper macro for creating instances of `JsonValue::Object`.
@@ -297,8 +347,8 @@ macro_rules! array {
297347
/// # #[macro_use] extern crate json;
298348
/// # fn main() {
299349
/// let data = object!{
300-
/// "foo" => 42,
301-
/// "bar" => false
350+
/// foo: 42,
351+
/// bar: false,
302352
/// };
303353
///
304354
/// assert_eq!(data["foo"], 42);
@@ -312,29 +362,68 @@ macro_rules! object {
312362
// Empty object.
313363
{} => ($crate::JsonValue::new_object());
314364

315-
// Non-empty object, no trailing comma.
316-
//
317-
// In this implementation, key/value pairs separated by commas.
318-
{ $( $key:expr => $value:expr ),* } => {
319-
$crate::object!( $(
320-
$key => $value,
321-
)* )
365+
// Handles for different types of keys
366+
(@ENTRY($( $k:expr => $v:expr, )*) $key:ident: $( $cont:tt )*) => {
367+
$crate::object!(@ENTRY($( $k => $v, )*) stringify!($key) => $($cont)*)
322368
};
369+
(@ENTRY($( $k:expr => $v:expr, )*) $key:literal: $( $cont:tt )*) => {
370+
$crate::object!(@ENTRY($( $k => $v, )*) $key => $($cont)*)
371+
};
372+
(@ENTRY($( $k:expr => $v:expr, )*) [$key:expr]: $( $cont:tt )*) => {
373+
$crate::object!(@ENTRY($( $k => $v, )*) $key => $($cont)*)
374+
};
375+
376+
// Handles for token tree values
377+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt, $( $cont:tt )+) => {
378+
$crate::object!(
379+
@ENTRY($( $k => $v, )* $key => $crate::value!($value), )
380+
$( $cont )*
381+
)
382+
};
383+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt,) => ({
384+
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
385+
});
386+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt) => ({
387+
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
388+
});
389+
390+
// Handles for expression values
391+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr, $( $cont:tt )+) => {
392+
$crate::object!(
393+
@ENTRY($( $k => $v, )* $key => $crate::value!($value), )
394+
$( $cont )*
395+
)
396+
};
397+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr,) => ({
398+
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
399+
});
323400

324-
// Non-empty object, trailing comma.
325-
//
326-
// In this implementation, the comma is part of the value.
327-
{ $( $key:expr => $value:expr, )* } => ({
328-
use $crate::object::Object;
401+
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr) => ({
402+
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
403+
});
329404

330-
let size = 0 $( + {let _ = $key; 1} )*;
331-
let mut object = Object::with_capacity(size);
405+
// Construct the actual object
406+
(@END $( $k:expr => $v:expr, )*) => ({
407+
let size = 0 $( + {let _ = || $k; 1} )*;
408+
let mut object = $crate::object::Object::with_capacity(size);
332409

333410
$(
334-
object.insert($key, $value.into());
411+
object.insert($k, $v.into());
335412
)*
336413

337414
$crate::JsonValue::Object(object)
338-
})
339-
}
415+
});
416+
417+
// Entry point to the macro
418+
($key:tt: $( $cont:tt )+) => {
419+
$crate::object!(@ENTRY() $key: $($cont)*)
420+
};
340421

422+
// Legacy macro
423+
($( $k:expr => $v:expr, )*) => {
424+
$crate::object!(@END $( $k => $crate::value!($v), )*)
425+
};
426+
($( $k:expr => $v:expr ),*) => {
427+
$crate::object!(@END $( $k => $crate::value!($v), )*)
428+
};
429+
}

src/object.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ impl<'a> ExactSizeIterator for IterMut<'a> {
654654
/// #
655655
/// # fn main() {
656656
/// let value = object!{
657-
/// "foo" => "bar"
657+
/// foo: "bar"
658658
/// };
659659
///
660660
/// if let JsonValue::Object(object) = value {

0 commit comments

Comments
 (0)