sample-std 0.2.1

Sampler definitions and implementations for st
Documentation
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<Json>),
    Map(HashMap<String, Json>),
}

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<usize>,
}

impl JsonSampler {
    fn string() -> impl Sample<Output = String> {
        Regex::new("[a-z]{20}")
    }

    fn array(&self, inner: JsonTree) -> impl Sample<Output = Vec<Json>> {
        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<JsonSampler>;

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<Tree>),
    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<Vec<Tree>> {
        match self {
            Tree::Branch(l) => Some(l),
            Tree::Leaf(_) => None,
        }
    }

    fn leaf(self) -> Option<usize> {
        match self {
            Self::Leaf(v) => Some(v),
            _ => None,
        }
    }
}

pub type TreeSampler = Box<dyn Sample<Output = Tree> + 'static>;

pub fn tree_sampler<LS>(depth: Range<usize>, branch: Range<usize>, leaf: LS) -> TreeSampler
where
    LS: Sample<Output = usize> + 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<JsonTree> = 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);
}