Skip to main content

snix_eval/value/
arbitrary.rs

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