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::{attrs::AttrsRep, NixAttrs, NixList, NixString, Value};
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)
45                .prop_map(|map| AttrsRep::Map(map.into_iter().collect()).into())
46        ]
47        .boxed()
48    }
49}
50
51impl Arbitrary for NixList {
52    type Parameters = <Value as Arbitrary>::Parameters;
53    type Strategy = BoxedStrategy<Self>;
54
55    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
56        vec(<Value as Arbitrary>::arbitrary_with(args), 0..100)
57            .prop_map(NixList::from)
58            .boxed()
59    }
60}
61
62impl Arbitrary for Value {
63    type Parameters = Parameters;
64    type Strategy = BoxedStrategy<Self>;
65
66    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
67        match args {
68            Parameters::Strategy(s) => s,
69            Parameters::Parameters {
70                generate_internal_values,
71                generate_functions,
72                generate_nested,
73            } => {
74                if generate_internal_values || generate_functions {
75                    todo!("Generating internal values and functions not implemented yet")
76                } else if generate_nested {
77                    non_internal_value().boxed()
78                } else {
79                    leaf_value().boxed()
80                }
81            }
82        }
83    }
84}
85
86fn leaf_value() -> impl Strategy<Value = Value> {
87    use Value::*;
88
89    prop_oneof![
90        Just(Null),
91        any::<bool>().prop_map(Bool),
92        any::<i64>().prop_map(Integer),
93        any::<f64>().prop_map(Float),
94        any::<NixString>().prop_map(String),
95        any::<OsString>().prop_map(|s| Path(Box::new(s.into()))),
96    ]
97}
98
99fn non_internal_value() -> impl Strategy<Value = Value> {
100    leaf_value().prop_recursive(3, 5, 5, |inner| {
101        prop_oneof![
102            NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs),
103            any_with::<NixList>(Parameters::Strategy(inner)).prop_map(Value::List)
104        ]
105    })
106}