Skip to content

Commit

Permalink
Better macros (#175)
Browse files Browse the repository at this point in the history
* Improved macros

* Restoring the logo

* Fix the link to logo in docs

* Version bump
  • Loading branch information
maciejhirsz authored Mar 17, 2020
1 parent 91c6da6 commit b99d38d
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 170 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "json"
version = "0.12.2"
version = "0.12.3"
authors = ["Maciej Hirsz <hello@maciej.codes>"]
description = "JSON implementation in Rust"
repository = "https://github.com/maciejhirsz/json-rust"
Expand Down
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# json-rust
![](https://raw.githubusercontent.com/maciejhirsz/json-rust/master/json-rust-logo-small.png)

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

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

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


## Why?

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

let instantiated = object!{
"code" => 200,
"success" => true,
"payload" => object!{
"features" => array![
// quotes on keys are optional
"code": 200,
success: true,
payload: {
features: [
"awesome",
"easyAPI",
"lowLearningCurve"
Expand All @@ -59,10 +57,10 @@ Using macros and indexing, it's easy to work with the data.

```rust
let mut data = object!{
"foo" => false,
"bar" => json::Null,
"answer" => 42,
"list" => array![json::Null, "world", true]
foo: false,
bar: null,
answer: 42,
list: [null, "world", true]
};

// Partial equality is implemented for most raw types:
Expand Down
Binary file added json-rust-logo-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
161 changes: 125 additions & 36 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! ![](http://terhix.com/doc/json-rust-logo-small.png)
//! ![](https://raw.githubusercontent.com/maciejhirsz/json-rust/master/json-rust-logo-small.png)
//!
//! # json-rust
//!
Expand Down Expand Up @@ -38,10 +38,11 @@
//! "#).unwrap();
//!
//! let instantiated = object!{
//! "code" => 200,
//! "success" => true,
//! "payload" => object!{
//! "features" => array![
//! // quotes on keys are optional
//! "code": 200,
//! success: true,
//! payload: {
//! features: [
//! "awesome",
//! "easyAPI",
//! "lowLearningCurve"
Expand All @@ -61,10 +62,10 @@
//! # #[macro_use] extern crate json;
//! # fn main() {
//! let mut data = object!{
//! "foo" => false,
//! "bar" => json::Null,
//! "answer" => 42,
//! "list" => array![json::Null, "world", true]
//! foo: false,
//! bar: null,
//! answer: 42,
//! list: [null, "world", true]
//! };
//!
//! // Partial equality is implemented for most raw types:
Expand Down Expand Up @@ -174,7 +175,7 @@
//! ```
//! # #[macro_use] extern crate json;
//! # fn main() {
//! let data = array!["foo", "bar", 100, true, json::Null];
//! let data = array!["foo", "bar", 100, true, null];
//! assert_eq!(data.dump(), r#"["foo","bar",100,true,null]"#);
//! # }
//! ```
Expand All @@ -185,9 +186,9 @@
//! # #[macro_use] extern crate json;
//! # fn main() {
//! let data = object!{
//! "name" => "John Doe",
//! "age" => 30,
//! "canJSON" => true
//! name: "John Doe",
//! age: 30,
//! canJSON: true
//! };
//! assert_eq!(
//! data.dump(),
Expand Down Expand Up @@ -279,16 +280,65 @@ pub fn stringify_pretty<T>(root: T, spaces: u16) -> String where T: Into<JsonVal
macro_rules! array {
[] => ($crate::JsonValue::new_array());

[ $( $item:expr ),* ] => ({
let size = 0 $( + {let _ = $item; 1} )*;
// Handles for token tree items
[@ITEM($( $i:expr, )*) $item:tt, $( $cont:tt )+] => {
$crate::array!(
@ITEM($( $i, )* $crate::value!($item), )
$( $cont )*
)
};
(@ITEM($( $i:expr, )*) $item:tt,) => ({
$crate::array!(@END $( $i, )* $crate::value!($item), )
});
(@ITEM($( $i:expr, )*) $item:tt) => ({
$crate::array!(@END $( $i, )* $crate::value!($item), )
});

// Handles for expression items
[@ITEM($( $i:expr, )*) $item:expr, $( $cont:tt )+] => {
$crate::array!(
@ITEM($( $i, )* $crate::value!($item), )
$( $cont )*
)
};
(@ITEM($( $i:expr, )*) $item:expr,) => ({
$crate::array!(@END $( $i, )* $crate::value!($item), )
});
(@ITEM($( $i:expr, )*) $item:expr) => ({
$crate::array!(@END $( $i, )* $crate::value!($item), )
});

// Construct the actual array
(@END $( $i:expr, )*) => ({
let size = 0 $( + {let _ = || $i; 1} )*;
let mut array = Vec::with_capacity(size);

$(
array.push($item.into());
array.push($i.into());
)*

$crate::JsonValue::Array(array)
})
});

// Entry point to the macro
($( $cont:tt )+) => {
$crate::array!(@ITEM() $($cont)*)
};
}

#[macro_export]
/// Helper crate for converting types into `JsonValue`. It's used
/// internally by the `object!` and `array!` macros.
macro_rules! value {
( null ) => { $crate::Null };
( [$( $token:tt )*] ) => {
// 10
$crate::array![ $( $token )* ]
};
( {$( $token:tt )*} ) => {
$crate::object!{ $( $token )* }
};
{ $value:expr } => { $value };
}

/// Helper macro for creating instances of `JsonValue::Object`.
Expand All @@ -297,8 +347,8 @@ macro_rules! array {
/// # #[macro_use] extern crate json;
/// # fn main() {
/// let data = object!{
/// "foo" => 42,
/// "bar" => false
/// foo: 42,
/// bar: false,
/// };
///
/// assert_eq!(data["foo"], 42);
Expand All @@ -312,29 +362,68 @@ macro_rules! object {
// Empty object.
{} => ($crate::JsonValue::new_object());

// Non-empty object, no trailing comma.
//
// In this implementation, key/value pairs separated by commas.
{ $( $key:expr => $value:expr ),* } => {
$crate::object!( $(
$key => $value,
)* )
// Handles for different types of keys
(@ENTRY($( $k:expr => $v:expr, )*) $key:ident: $( $cont:tt )*) => {
$crate::object!(@ENTRY($( $k => $v, )*) stringify!($key) => $($cont)*)
};
(@ENTRY($( $k:expr => $v:expr, )*) $key:literal: $( $cont:tt )*) => {
$crate::object!(@ENTRY($( $k => $v, )*) $key => $($cont)*)
};
(@ENTRY($( $k:expr => $v:expr, )*) [$key:expr]: $( $cont:tt )*) => {
$crate::object!(@ENTRY($( $k => $v, )*) $key => $($cont)*)
};

// Handles for token tree values
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt, $( $cont:tt )+) => {
$crate::object!(
@ENTRY($( $k => $v, )* $key => $crate::value!($value), )
$( $cont )*
)
};
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt,) => ({
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
});
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:tt) => ({
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
});

// Handles for expression values
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr, $( $cont:tt )+) => {
$crate::object!(
@ENTRY($( $k => $v, )* $key => $crate::value!($value), )
$( $cont )*
)
};
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr,) => ({
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
});

// Non-empty object, trailing comma.
//
// In this implementation, the comma is part of the value.
{ $( $key:expr => $value:expr, )* } => ({
use $crate::object::Object;
(@ENTRY($( $k:expr => $v:expr, )*) $key:expr => $value:expr) => ({
$crate::object!(@END $( $k => $v, )* $key => $crate::value!($value), )
});

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

$(
object.insert($key, $value.into());
object.insert($k, $v.into());
)*

$crate::JsonValue::Object(object)
})
}
});

// Entry point to the macro
($key:tt: $( $cont:tt )+) => {
$crate::object!(@ENTRY() $key: $($cont)*)
};

// Legacy macro
($( $k:expr => $v:expr, )*) => {
$crate::object!(@END $( $k => $crate::value!($v), )*)
};
($( $k:expr => $v:expr ),*) => {
$crate::object!(@END $( $k => $crate::value!($v), )*)
};
}
2 changes: 1 addition & 1 deletion src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ impl<'a> ExactSizeIterator for IterMut<'a> {
/// #
/// # fn main() {
/// let value = object!{
/// "foo" => "bar"
/// foo: "bar"
/// };
///
/// if let JsonValue::Object(object) = value {
Expand Down
Loading

0 comments on commit b99d38d

Please sign in to comment.