diff options
Diffstat (limited to 'tvix/eval/src/value/arbitrary.rs')
-rw-r--r-- | tvix/eval/src/value/arbitrary.rs | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/tvix/eval/src/value/arbitrary.rs b/tvix/eval/src/value/arbitrary.rs new file mode 100644 index 000000000000..a2e8cb899c92 --- /dev/null +++ b/tvix/eval/src/value/arbitrary.rs @@ -0,0 +1,106 @@ +//! Support for configurable generation of arbitrary nix values + +use imbl::proptest::{ord_map, vector}; +use proptest::{prelude::*, strategy::BoxedStrategy}; +use std::{ffi::OsString, path::PathBuf}; + +use super::{attrs::AttrsRep, NixAttrs, NixList, NixString, Value}; + +#[derive(Clone)] +pub enum Parameters { + Strategy(BoxedStrategy<Value>), + 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<Self>; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + prop_oneof![ + // Empty attrs representation + Just(Self(AttrsRep::Empty)), + // KV representation (name/value pairs) + ( + any_with::<Value>(args.clone()), + any_with::<Value>(args.clone()) + ) + .prop_map(|(name, value)| Self(AttrsRep::KV { name, value })), + // Map representation + ord_map(NixString::arbitrary(), Value::arbitrary_with(args), 0..100) + .prop_map(|map| Self(AttrsRep::Im(map))) + ] + .boxed() + } +} + +impl Arbitrary for NixList { + type Parameters = <Value as Arbitrary>::Parameters; + type Strategy = BoxedStrategy<Self>; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + vector(<Value as Arbitrary>::arbitrary_with(args), 0..100) + .prop_map(NixList::from) + .boxed() + } +} + +impl Arbitrary for Value { + type Parameters = Parameters; + type Strategy = BoxedStrategy<Self>; + + 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<Value = Value> { + use Value::*; + + prop_oneof![ + Just(Null), + any::<bool>().prop_map(Bool), + any::<i64>().prop_map(Integer), + any::<f64>().prop_map(Float), + any::<Box<NixString>>().prop_map(String), + any::<OsString>().prop_map(|s| Path(PathBuf::from(s).into_boxed_path())), + ] +} + +fn non_internal_value() -> impl Strategy<Value = Value> { + leaf_value().prop_recursive(3, 5, 5, |inner| { + prop_oneof![ + NixAttrs::arbitrary_with(Parameters::Strategy(inner.clone())).prop_map(Value::attrs), + any_with::<NixList>(Parameters::Strategy(inner)).prop_map(Value::List) + ] + }) +} |