#[macro_export]
macro_rules! json {
(null) => { $crate::Value::Null };
([]) => { $crate::Value::Array(::std::vec::Vec::new()) };
([ $($elem:tt)+ ]) => {
$crate::Value::Array($crate::json_array![[] $($elem)+])
};
({}) => { $crate::Value::Object(::std::vec::Vec::new()) };
({ $($member:tt)+ }) => {
$crate::Value::Object($crate::json_object![[] $($member)+])
};
($other:expr) => { $crate::Value::from($other) };
}
#[macro_export]
#[doc(hidden)]
macro_rules! json_array {
([$($done:expr),*]) => { ::std::vec![$($done),*] };
([$($done:expr),*] ,) => { ::std::vec![$($done),*] };
([$($done:expr),*] [$($arr:tt)*] , $($rest:tt)*) => {
$crate::json_array![[$($done,)* $crate::json!([$($arr)*])] $($rest)*]
};
([$($done:expr),*] [$($arr:tt)*]) => {
$crate::json_array![[$($done,)* $crate::json!([$($arr)*])]]
};
([$($done:expr),*] {$($obj:tt)*} , $($rest:tt)*) => {
$crate::json_array![[$($done,)* $crate::json!({$($obj)*})] $($rest)*]
};
([$($done:expr),*] {$($obj:tt)*}) => {
$crate::json_array![[$($done,)* $crate::json!({$($obj)*})]]
};
([$($done:expr),*] null , $($rest:tt)*) => {
$crate::json_array![[$($done,)* $crate::Value::Null] $($rest)*]
};
([$($done:expr),*] null) => {
$crate::json_array![[$($done,)* $crate::Value::Null]]
};
([$($done:expr),*] $next:expr , $($rest:tt)*) => {
$crate::json_array![[$($done,)* $crate::json!($next)] $($rest)*]
};
([$($done:expr),*] $last:expr) => {
$crate::json_array![[$($done,)* $crate::json!($last)]]
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! json_object {
([$($done:expr),*]) => { ::std::vec![$($done),*] };
([$($done:expr),*] ,) => { ::std::vec![$($done),*] };
([$($done:expr),*] $key:tt : [$($arr:tt)*] , $($rest:tt)*) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!([$($arr)*]))] $($rest)*]
};
([$($done:expr),*] $key:tt : [$($arr:tt)*]) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!([$($arr)*]))]]
};
([$($done:expr),*] $key:tt : {$($obj:tt)*} , $($rest:tt)*) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!({$($obj)*}))] $($rest)*]
};
([$($done:expr),*] $key:tt : {$($obj:tt)*}) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!({$($obj)*}))]]
};
([$($done:expr),*] $key:tt : null , $($rest:tt)*) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::Value::Null)] $($rest)*]
};
([$($done:expr),*] $key:tt : null) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::Value::Null)]]
};
([$($done:expr),*] $key:tt : $val:expr , $($rest:tt)*) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!($val))] $($rest)*]
};
([$($done:expr),*] $key:tt : $val:expr) => {
$crate::json_object![[$($done,)* ($crate::json_key!($key), $crate::json!($val))]]
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! json_key {
(($key:expr)) => {
::std::string::ToString::to_string(&$key)
};
($key:literal) => {
::std::string::ToString::to_string(&$key)
};
($key:tt) => {
::std::stringify!($key).to_string()
};
}
#[cfg(test)]
mod tests {
use crate::Value;
#[test]
fn builds_scalars_and_empties() {
assert_eq!(json!(null), Value::Null);
assert_eq!(json!(true), Value::Bool(true));
assert_eq!(json!(42), Value::Int(42));
assert_eq!(json!(2.5), Value::Float(2.5));
assert_eq!(json!("s"), Value::Str("s".into()));
assert_eq!(json!([]).to_json(), "[]");
assert_eq!(json!({}).to_json(), "{}");
}
#[test]
fn nests_arrays_and_objects_and_tolerates_trailing_commas() {
let v = json!({
"a": [1, 2, [3, {"deep": true}]],
"b": { "c": { "d": null } },
});
assert_eq!(
v.to_json(),
r#"{"a":[1,2,[3,{"deep":true}]],"b":{"c":{"d":null}}}"#
);
assert_eq!(json!([1, 2, 3,]).to_json(), "[1,2,3]");
}
#[test]
fn object_keys_preserve_insertion_order() {
let v = json!({ "z": 1, "a": 2, "m": 3 });
assert_eq!(v.to_json(), r#"{"z":1,"a":2,"m":3}"#);
}
#[test]
fn interpolates_expressions_in_key_and_value_position() {
let key = "dynamic";
let n = 3_i64;
let owned = String::from("owned");
let v = json!({ (key): n * 2, "s": owned, "computed": n > 1 });
assert_eq!(v.to_json(), r#"{"dynamic":6,"s":"owned","computed":true}"#);
}
#[test]
fn nests_an_existing_value_through_the_identity_conversion() {
let inner = json!({ "already": "built" });
let outer = json!({ "wrapped": inner });
assert_eq!(outer.to_json(), r#"{"wrapped":{"already":"built"}}"#);
}
#[test]
fn converts_options_vecs_and_slices() {
let some: Option<i64> = Some(7);
let none: Option<i64> = None;
let xs: Vec<i64> = vec![1, 2];
let slice: &[&str] = &["a", "b"];
let v = json!({ "some": some, "none": none, "xs": xs, "slice": slice });
assert_eq!(
v.to_json(),
r#"{"some":7,"none":null,"xs":[1,2],"slice":["a","b"]}"#
);
}
#[test]
fn collects_pairs_into_an_object() {
let v: Value = [("a", 1_i64), ("b", 2)].into_iter().collect();
assert_eq!(v.to_json(), r#"{"a":1,"b":2}"#);
}
#[test]
fn integer_widths_all_convert() {
assert_eq!(json!(1_i8), Value::Int(1));
assert_eq!(json!(1_u32), Value::Int(1));
assert_eq!(json!(-1_isize), Value::Int(-1));
assert_eq!(json!(1.5_f32), Value::Float(1.5));
}
}