imbl-value 0.4.3

A JSON `Value` type built on `imbl`
Documentation
use proptest::prelude::*;
use proptest_derive::Arbitrary;
use yasi::InternedString;

use crate::Value;

impl Arbitrary for Value {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;
    fn arbitrary() -> Self::Strategy {
        value_strategy()
    }
    fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
        value_strategy()
    }
}

pub fn value_strategy() -> BoxedStrategy<Value> {
    let leaf = proptest::prop_oneof![
        Just(Value::Null),
        any::<bool>().prop_map(Value::Bool),
        number_strategy(),
        ".*".prop_map(|s| Value::from(s)),
    ];
    leaf.prop_recursive(8, 256, 10, |inner| {
        prop_oneof![array_strategy(inner.clone()), object_strategy(inner)]
    })
    .boxed()
}

pub fn number_strategy() -> BoxedStrategy<Value> {
    #[derive(Debug, Arbitrary)]
    enum Number {
        PosInt(u64),
        NegInt(i64),
        Float(f64),
    }
    Number::arbitrary()
        .prop_map::<serde_json::Number, _>(|n| match n {
            Number::PosInt(a) => a.into(),
            Number::NegInt(a) => a.into(),
            Number::Float(a) => {
                serde_json::Number::from_f64(a).unwrap_or(serde_json::Number::from(0))
            }
        })
        .prop_map(Value::Number)
        .boxed()
}

pub fn array_strategy(inner: impl Strategy<Value = Value> + 'static) -> BoxedStrategy<Value> {
    imbl::proptest::vector(inner, 0..10)
        .prop_map(Value::Array)
        .boxed()
}

pub fn object_strategy(inner: impl Strategy<Value = Value> + 'static) -> BoxedStrategy<Value> {
    prop::collection::hash_map(".*", inner, 0..10)
        .prop_map(|map| {
            map.into_iter()
                .map(|(k, v)| (InternedString::intern(k), v))
                .collect()
        })
        .prop_map(Value::Object)
        .boxed()
}