tvix_eval/value/
arbitrary.rs

1//! Support for configurable generation of arbitrary nix values
2
3use proptest::collection::{hash_map, vec};
4use proptest::{prelude::*, strategy::BoxedStrategy};
5use rustc_hash::FxHashMap;
6use std::ffi::OsString;
7
8use super::{NixAttrs, NixList, NixString, Value};
9
10#[derive(Clone)]
11pub enum Parameters {
12    Strategy(BoxedStrategy<Value>),
13    Parameters {
14        generate_internal_values: bool,
15        generate_functions: bool,
16        generate_nested: bool,
17    },
18}
19
20impl Default for Parameters {
21    fn default() -> Self {
22        Self::Parameters {
23            generate_internal_values: false,
24            generate_functions: false,
25            generate_nested: true,
26        }
27    }
28}
29
30impl Arbitrary for NixAttrs {
31    type Parameters = Parameters;
32    type Strategy = BoxedStrategy<Self>;
33
34    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
35        hash_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100)
36            .prop_map(|map| FxHashMap::from_iter(map).into())
37            .boxed()
38    }
39}
40
41impl Arbitrary for NixList {
42    type Parameters = <Value as Arbitrary>::Parameters;
43    type Strategy = BoxedStrategy<Self>;
44
45    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
46        vec(<Value as Arbitrary>::arbitrary_with(args), 0..100)
47            .prop_map(NixList::from)
48            .boxed()
49    }
50}
51
52impl Arbitrary for Value {
53    type Parameters = Parameters;
54    type Strategy = BoxedStrategy<Self>;
55
56    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
57        match args {
58            Parameters::Strategy(s) => s,
59            Parameters::Parameters {
60                generate_internal_values,
61                generate_functions,
62                generate_nested,
63            } => {
64                if generate_internal_values || generate_functions {
65                    todo!("Generating internal values and functions not implemented yet")
66                } else if generate_nested {
67                    non_internal_value().boxed()
68                } else {
69                    leaf_value().boxed()
70                }
71            }
72        }
73    }
74}
75
76fn leaf_value() -> impl Strategy<Value = Value> {
77    use Value::*;
78
79    prop_oneof![
80        Just(Null),
81        any::<bool>().prop_map(Bool),
82        any::<i64>().prop_map(Integer),
83        any::<f64>().prop_map(Float),
84        any::<NixString>().prop_map(String),
85        any::<OsString>().prop_map(|s| Path(Box::new(s.into()))),
86    ]
87}
88
89fn non_internal_value() -> impl Strategy<Value = Value> {
90    leaf_value().prop_recursive(3, 5, 5, |inner| {
91        prop_oneof![
92            NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs),
93            any_with::<NixList>(Parameters::Strategy(inner)).prop_map(Value::List)
94        ]
95    })
96}