//! Support for configurable generation of arbitrary nix values use proptest::collection::{btree_map, vec}; use proptest::{prelude::*, strategy::BoxedStrategy}; use std::ffi::OsString; use super::{attrs::AttrsRep, NixAttrs, NixList, NixString, Value}; #[derive(Clone)] pub enum Parameters { Strategy(BoxedStrategy), Parameters { generate_internal_values: bool, generate_functions: bool, generate_nested: bool, }, } impl Default for Parameters { fn default() -> Self { Self::Parameters { generate_internal_values: false, generate_functions: false, generate_nested: true, } } } impl Arbitrary for NixAttrs { type Parameters = Parameters; type Strategy = BoxedStrategy; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { prop_oneof![ // Empty attrs representation Just(AttrsRep::Empty.into()), // KV representation (name/value pairs) ( any_with::(args.clone()), any_with::(args.clone()) ) .prop_map(|(name, value)| AttrsRep::KV { name, value }.into()), // Map representation btree_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100) .prop_map(|map| AttrsRep::Map(map).into()) ] .boxed() } } impl Arbitrary for NixList { type Parameters = ::Parameters; type Strategy = BoxedStrategy; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { vec(::arbitrary_with(args), 0..100) .prop_map(NixList::from) .boxed() } } impl Arbitrary for Value { type Parameters = Parameters; type Strategy = BoxedStrategy; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { match args { Parameters::Strategy(s) => s, Parameters::Parameters { generate_internal_values, generate_functions, generate_nested, } => { if generate_internal_values || generate_functions { todo!("Generating internal values and functions not implemented yet") } else if generate_nested { non_internal_value().boxed() } else { leaf_value().boxed() } } } } } fn leaf_value() -> impl Strategy { use Value::*; prop_oneof![ Just(Null), any::().prop_map(Bool), any::().prop_map(Integer), any::().prop_map(Float), any::().prop_map(String), any::().prop_map(|s| Path(Box::new(s.into()))), ] } fn non_internal_value() -> impl Strategy { leaf_value().prop_recursive(3, 5, 5, |inner| { prop_oneof![ NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs), any_with::(Parameters::Strategy(inner)).prop_map(Value::List) ] }) }