Skip to main content

hegel/extras/serde_json/
generators.rs

1use std::collections::HashMap;
2
3use serde_json::{Number, Value};
4
5use crate::generators::{self as gs, BoxedGenerator, DefaultGenerator, Generator};
6
7/// Generator for [`serde_json::Number`] values. Created by [`numbers()`].
8///
9/// Produces `Number` values backed by an `i64`, `u64`, or finite `f64`.
10/// JSON numbers cannot represent NaN or infinity, so the float branch is
11/// constrained to finite values.
12pub struct NumberGenerator {
13    inner: BoxedGenerator<'static, Number>,
14}
15
16impl Generator<Number> for NumberGenerator {
17    fn do_draw(&self, tc: &crate::TestCase) -> Number {
18        self.inner.do_draw(tc)
19    }
20
21    fn as_basic(&self) -> Option<gs::BasicGenerator<'_, Number>> {
22        self.inner.as_basic()
23    }
24}
25
26/// Generate [`serde_json::Number`] values.
27///
28/// See [`NumberGenerator`] for details.
29///
30/// # Example
31///
32/// ```no_run
33/// use hegel::extras::serde_json as json_gs;
34///
35/// #[hegel::test]
36/// fn my_test(tc: hegel::TestCase) {
37///     let n = tc.draw(json_gs::numbers());
38///     assert!(n.as_i64().is_some() || n.as_u64().is_some() || n.as_f64().is_some());
39/// }
40/// ```
41pub fn numbers() -> NumberGenerator {
42    let inner = gs::one_of([
43        gs::integers::<i64>().map(Number::from).boxed(),
44        gs::integers::<u64>().map(Number::from).boxed(),
45        gs::floats::<f64>()
46            .allow_nan(false)
47            .allow_infinity(false)
48            .map(|f| Number::from_f64(f).unwrap())
49            .boxed(),
50    ])
51    .boxed();
52    NumberGenerator { inner }
53}
54
55/// Generator for [`serde_json::Value`] values. Created by [`values()`].
56pub struct ValueGenerator {
57    inner: BoxedGenerator<'static, Value>,
58}
59
60impl Generator<Value> for ValueGenerator {
61    fn do_draw(&self, tc: &crate::TestCase) -> Value {
62        self.inner.do_draw(tc)
63    }
64}
65
66/// Generate [`serde_json::Value`] values.
67///
68/// See [`ValueGenerator`] for details.
69///
70/// # Example
71///
72/// ```no_run
73/// use hegel::extras::serde_json as json_gs;
74///
75/// #[hegel::test]
76/// fn my_test(tc: hegel::TestCase) {
77///     let v = tc.draw(json_gs::values());
78///     // round-trip through serde_json
79///     let s = serde_json::to_string(&v).unwrap();
80///     let _: serde_json::Value = serde_json::from_str(&s).unwrap();
81/// }
82/// ```
83pub fn values() -> ValueGenerator {
84    let def = gs::deferred::<Value>();
85    let handle = def.generator();
86
87    let recursive = gs::one_of([
88        gs::just(Value::Null).boxed(),
89        gs::booleans().map(Value::Bool).boxed(),
90        numbers().map(Value::Number).boxed(),
91        <String as DefaultGenerator>::default_generator()
92            .map(Value::String)
93            .boxed(),
94        gs::vecs(handle.clone()).map(Value::Array).boxed(),
95        gs::hashmaps(
96            <String as DefaultGenerator>::default_generator(),
97            handle.clone(),
98        )
99        .map(|m: HashMap<String, Value>| Value::Object(m.into_iter().collect()))
100        .boxed(),
101    ])
102    .boxed();
103
104    def.set(recursive);
105
106    ValueGenerator { inner: handle }
107}
108
109/// Generator for [`Box<RawValue>`](serde_json::value::RawValue) values.
110/// Created by [`raw_values()`].
111///
112/// The generated values are guaranteed to be valid json.
113#[cfg(feature = "serde_json_raw_value")]
114pub struct RawValueGenerator {
115    inner: BoxedGenerator<'static, Box<serde_json::value::RawValue>>,
116}
117
118#[cfg(feature = "serde_json_raw_value")]
119impl Generator<Box<serde_json::value::RawValue>> for RawValueGenerator {
120    fn do_draw(&self, tc: &crate::TestCase) -> Box<serde_json::value::RawValue> {
121        self.inner.do_draw(tc)
122    }
123}
124
125/// Generate [`Box<RawValue>`](serde_json::value::RawValue) values.
126///
127/// # Example
128///
129/// ```no_run
130/// use hegel::extras::serde_json as json_gs;
131///
132/// #[hegel::test]
133/// fn my_test(tc: hegel::TestCase) {
134///     let r = tc.draw(json_gs::raw_values());
135///     // The generated value is always valid JSON.
136///     let _: serde_json::Value = serde_json::from_str(r.get()).unwrap();
137/// }
138/// ```
139#[cfg(feature = "serde_json_raw_value")]
140pub fn raw_values() -> RawValueGenerator {
141    let inner = values()
142        .map(|v| {
143            serde_json::value::RawValue::from_string(serde_json::to_string(&v).unwrap()).unwrap()
144        })
145        .boxed();
146    RawValueGenerator { inner }
147}