use super::string::JSONString;
use super::JSONValue;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::io;
fn write_object_entry<W, K, V>(w: &mut W, key: &K, value: &V) -> io::Result<()>
where
W: io::Write,
K: JSONString,
V: JSONValue,
{
key.write_json(w)?;
w.write_all(b":")?;
value.write_json(w)
}
fn write_object<'a, W, K, V, I>(w: &mut W, iter: &mut I) -> io::Result<()>
where
W: io::Write,
K: JSONString,
V: JSONValue,
V: 'a,
K: 'a,
I: Iterator<Item = (&'a K, &'a V)>,
{
w.write_all(b"{")?;
if let Some((key, value)) = iter.next() {
write_object_entry(w, key, value)?;
for (key, value) in iter {
w.write_all(b",")?;
write_object_entry(w, key, value)?;
}
}
w.write_all(b"}")
}
pub struct ToJSONObject<K, V, I>(pub I)
where
K: JSONString,
V: JSONValue,
for<'a> &'a I: IntoIterator<Item = &'a (K, V)>;
impl<K, V, I> JSONValue for ToJSONObject<K, V, I>
where
K: JSONString,
V: JSONValue,
for<'a> &'a I: IntoIterator<Item = &'a (K, V)>,
{
fn write_json<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
let mut iter = (&self.0).into_iter().map(|(k, v)| (k, v)); write_object(w, &mut iter)
}
}
impl<K: JSONString + Eq + Hash, V: JSONValue, S: BuildHasher> JSONValue for HashMap<K, V, S> {
fn write_json<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
write_object(w, &mut self.iter())
}
}
pub trait JSONObject: JSONValue {
fn write_json_ending<W: io::Write>(&self, f: &mut W, first: bool) -> io::Result<()>;
#[inline]
fn write_json_full<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
self.write_json_ending(w, true)
}
}
pub struct JSONObjectEntry<K: JSONString, V: JSONValue, U: JSONObject> {
pub key: K,
pub value: V,
pub next: U,
}
impl<K: JSONString, V: JSONValue, U: JSONObject> JSONObject for JSONObjectEntry<K, V, U> {
#[inline(always)]
fn write_json_ending<W: io::Write>(&self, w: &mut W, first: bool) -> io::Result<()> {
w.write_all(if first { b"{" } else { b"," })?;
self.key.write_json(w)?;
w.write_all(b":")?;
self.value.write_json(w)?;
self.next.write_json_ending(w, false)
}
}
impl<K: JSONString, V: JSONValue, U: JSONObject> JSONValue for JSONObjectEntry<K, V, U> {
#[inline(always)]
fn write_json<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
self.write_json_full(w)
}
}
pub struct JSONObjectEnd;
impl JSONObject for JSONObjectEnd {
#[inline(always)]
fn write_json_ending<W: io::Write>(&self, w: &mut W, first: bool) -> io::Result<()> {
w.write_all(if first { b"{}" } else { b"}" })
}
}
impl JSONValue for JSONObjectEnd {
fn write_json<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
self.write_json_full(w)
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! inlined_json_object {
(key : $key:ident, value : $value:expr, next : $next:expr) => {{
use $crate::object::JSONObject;
use $crate::JSONValue;
struct InlinedJSONObjectEntry<V: JSONValue, U: JSONObject> {
value: V,
next: U,
}
impl<V: JSONValue, U: JSONObject> JSONObject for InlinedJSONObjectEntry<V, U> {
#[inline(always)]
fn write_json_ending<W: ::std::io::Write>(
&self,
w: &mut W,
first: bool,
) -> ::std::io::Result<()> {
w.write_all(
if first {
concat!("{\"", stringify!($key), "\":")
} else {
concat!(",\"", stringify!($key), "\":")
}
.as_bytes(),
)?;
self.value.write_json(w)?;
self.next.write_json_ending(w, false)
}
}
impl<V: JSONValue, U: JSONObject> JSONValue for InlinedJSONObjectEntry<V, U> {
fn write_json<W: ::std::io::Write>(&self, w: &mut W) -> ::std::io::Result<()> {
self.write_json_full(w)
}
}
InlinedJSONObjectEntry {
value: $value,
next: $next,
}
}};
}
#[macro_export]
macro_rules! json_object {
() => { $crate::object::JSONObjectEnd{} };
($key:ident : null, $($rest:tt)*) => { json_object!($key : (), $($rest)*) };
($key:ident : true, $($rest:tt)*) => { json_object!($key : $crate::base_types::JSONtrue, $($rest)*) };
($key:ident : false, $($rest:tt)*) => { json_object!($key : $crate::base_types::JSONfalse, $($rest)*) };
($key:ident : $value:expr, $($rest:tt)*) => {
inlined_json_object!{
key: $key,
value: $value,
next: json_object!($($rest)*)
}
};
([$key:expr] : $value:expr, $($rest:tt)*) => {
$crate::object::JSONObjectEntry {
key: $key,
value: $value,
next: json_object!($($rest)*)
}
};
($key:ident, $($rest:tt)*) => { json_object!($key : $key, $($rest)*) };
($key:ident : $value:ident) => { json_object!($key:$value,) };
($key:ident : $value:expr) => { json_object!($key:$value,) };
([$key:expr] : $value:expr) => { json_object!([$key]:$value,) };
($key:ident) => { json_object!($key,) };
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
assert_eq!("{}", JSONObjectEnd.to_json_string());
assert_eq!("{}", json_object!().to_json_string());
}
#[test]
fn test_single_pair() {
assert_eq!(
r#"{"x":{}}"#,
json_object!(x: json_object!()).to_json_string()
);
assert_eq!(
r#"{"x":{}}"#,
json_object!(x: json_object!(),).to_json_string()
);
}
#[test]
fn test_two_pairs() {
assert_eq!(
r#"{"x":{},"y":{}}"#,
json_object! {
x : json_object!(),
y : json_object!()
}
.to_json_string()
);
}
#[test]
fn test_nested() {
assert_eq!(
r#"{"x":{"y":{}}}"#,
json_object! {
x : json_object! {
y : json_object!()
}
}
.to_json_string()
);
}
#[test]
fn test_dynamic_keys() {
let x = "x";
let y = String::from("y");
assert_eq!(
r#"{"x":{"y":{}}}"#,
json_object! {
[x] : json_object! {
[y] : json_object!()
}
}
.to_json_string()
);
}
#[test]
fn test_hashmap() {
let mut map = HashMap::new();
map.insert("x", 1);
map.insert("y", 2);
let expected = vec![r#"{"x":1,"y":2}"#, r#"{"y":2,"x":1}"#];
assert!(expected.contains(&&map.to_json_string()[..]));
}
#[test]
fn test_zero_size() {
use std::mem::size_of_val;
let json_obj = json_object! {
null: null,
nested: json_object! {
also_null: (),
bool : true,
deeper: json_object! {
other_bool: false
}
}
};
assert_eq!(0, size_of_val(&json_obj));
}
}