use std::collections::HashMap; use std::ops::Range; use once_cell::sync::Lazy; use sample_std::{ recursive::{Recursion, RecursiveSampler}, sampler_choice, Random, Regex, Sample, VecSampler, }; use sample_test::sample_test; #[derive(Clone, Debug)] pub enum Json { Null, Bool(bool), Number(f64), String(String), Array(Vec), Map(HashMap), } impl Json { fn depth(&self) -> usize { match self { Json::Array(bs) => bs.iter().map(Json::depth).max().unwrap_or(1), _ => 0, } } } #[derive(Debug, Clone)] pub struct JsonSampler { branch: Range, } impl JsonSampler { fn string() -> impl Sample { Regex::new("[a-z]{20}") } fn array(&self, inner: JsonTree) -> impl Sample> { VecSampler { length: self.branch.clone(), el: inner, } } } impl Sample for JsonSampler { type Output = Json; fn generate(&mut self, g: &mut Random) -> Json { match g.gen_range(0..=3) { 0 => Json::Bool(g.arbitrary()), 1 => Json::Number(g.arbitrary()), 2 => Json::String(Self::string().generate(g)), _ => Json::Null, } } } pub type JsonTree = RecursiveSampler; impl Recursion for JsonSampler { type Output = Json; fn recurse(&self, g: &mut Random, inner: JsonTree) -> Self::Output { match g.gen_range(0..=1) { _ => Json::Array(self.array(inner).generate(g)), } } } #[derive(Debug, Clone, PartialEq)] pub enum Tree { Branch(Vec), Leaf(usize), } impl Tree { fn depth(&self) -> usize { match self { Tree::Branch(bs) => 1 + bs.iter().map(Tree::depth).max().unwrap_or(0), Tree::Leaf(..) => 0, } } fn level(self) -> Option> { match self { Tree::Branch(l) => Some(l), Tree::Leaf(_) => None, } } fn leaf(self) -> Option { match self { Self::Leaf(v) => Some(v), _ => None, } } } pub type TreeSampler = Box + 'static>; pub fn tree_sampler(depth: Range, branch: Range, leaf: LS) -> TreeSampler where LS: Sample + Clone + 'static, { let leaf = Box::new(leaf.try_convert(Tree::Leaf, Tree::leaf)); let mut inner: TreeSampler = leaf.clone(); for ix in (0..(depth.end - 1)).rev() { let el: TreeSampler = if ix > depth.start { Box::new(sampler_choice([leaf.clone(), inner])) } else { inner }; let length = if ix < depth.start - 1 { 1..depth.end } else { branch.clone() }; let level = VecSampler { length, el }; inner = Box::new(level.try_convert(Tree::Branch, Tree::level)) } inner } #[sample_test] fn tree_bounds(#[sample(tree_sampler(2..5, 0..3, 0..100))] tree: Tree) { assert!(tree.depth() >= 2); assert!(tree.depth() < 5); } static JSON: Lazy = Lazy::new(|| JsonTree { depth: Some(0..3), node: JsonSampler { branch: 1..10 }, }); #[sample_test] fn json(#[sample(JSON.clone())] json: Json) { assert!(json.depth() < 4); }