ijson 0.1.7

A more memory efficient replacement for serde_json::Value
Documentation
//! Exercises the trait impls generated by `value_subtype_impls!` (the `IValue` ↔ subtype
//! conversions: `AsRef`/`AsMut`/`Borrow`/`BorrowMut`/`From`/`TryFrom`) and the public `ijson!`
//! construction macro. Both are public surface that the rest of the suite only touches
//! incidentally.

use std::borrow::{Borrow, BorrowMut};
use std::convert::TryFrom;

use ijson::{ijson, IArray, INumber, IObject, IString, IValue};

/// Runs every conversion `value_subtype_impls!` generates for one subtype, in both the
/// tag-matches (success) and tag-mismatches (error) directions. `$wrong` is a value of a
/// *different* JSON type, so all four `TryFrom` conversions must reject it.
macro_rules! check_conversions {
    ($subtype:expr, $t:ty, $wrong:expr) => {{
        let mut subtype: $t = $subtype;

        // `AsRef`/`AsMut<IValue>` and `Borrow`/`BorrowMut<IValue>` all expose the wrapped value.
        let _: &IValue = subtype.as_ref();
        let _: &IValue = subtype.borrow();
        let _: &mut IValue = subtype.as_mut();
        let _: &mut IValue = subtype.borrow_mut();

        // `From<$t> for IValue`, then every `TryFrom` back, with the tag matching.
        let value = IValue::from(subtype.clone());
        assert_eq!(<$t>::try_from(value.clone()).unwrap(), subtype);
        assert_eq!(*<&$t>::try_from(&value).unwrap(), subtype);
        let mut value_mut = value.clone();
        assert_eq!(*<&mut $t>::try_from(&mut value_mut).unwrap(), subtype);

        // The same three `TryFrom`s must reject a value whose tag does not match. The owned
        // form hands the original `IValue` back as the error, so it is not lost.
        let mut wrong = $wrong;
        assert!(<$t>::try_from(wrong.clone()).is_err());
        assert!(<&$t>::try_from(&wrong).is_err());
        assert!(<&mut $t>::try_from(&mut wrong).is_err());
    }};
}

#[test]
fn subtype_value_conversions() {
    let mut obj = IObject::new();
    obj.insert("k", IValue::from(1));
    check_conversions!(obj, IObject, IValue::NULL);

    let mut arr = IArray::new();
    arr.push(IValue::from(1));
    check_conversions!(arr, IArray, IValue::NULL);

    check_conversions!(IString::intern("hello"), IString, IValue::NULL);
    check_conversions!(INumber::from(42), INumber, IValue::from("not a number"));
}

#[test]
fn ijson_macro_builds_every_shape() {
    let v = ijson!({
        "null": null,
        "yes": true,
        "no": false,
        "nested": [1, 2, [3, 4], { "deep": 5 }],
        "text": "s",
        "empty_arr": [],
        "empty_obj": {}
    });

    let o = v.as_object().expect("top level is an object");
    assert!(o["null"].is_null());
    assert!(o["yes"].is_true());
    assert!(o["no"].is_false());
    assert_eq!(o["text"].as_string().unwrap().as_str(), "s");
    assert_eq!(o["empty_arr"].as_array().unwrap().len(), 0);
    assert_eq!(o["empty_obj"].as_object().unwrap().len(), 0);

    let nested = o["nested"].as_array().expect("nested is an array");
    assert_eq!(nested.len(), 4);
    assert_eq!(nested[0], IValue::from(1));
    assert_eq!(nested[2].as_array().unwrap().len(), 2);
    assert_eq!(nested[3].as_object().unwrap()["deep"], IValue::from(5));

    // The trailing `$other:expr` arm: an arbitrary expression (here a variable) serialized
    // through `to_value`.
    let count = 7;
    let with_var = ijson!({ "n": count });
    assert_eq!(with_var.as_object().unwrap()["n"], IValue::from(7));

    // Top-level array and bare-scalar forms.
    let arr = ijson!([true, null, "x"]);
    assert_eq!(arr.as_array().unwrap().len(), 3);
    assert_eq!(ijson!(42), IValue::from(42));
}