Function contrafact::facts::prism

source ·
pub fn prism<'a, O, T, P>(
    label: impl ToString,
    prism: P,
    inner_fact: impl Fact<'a, T>
) -> impl Fact<'a, O>where
    O: Target<'a>,
    T: Target<'a>,
    P: 'a + Send + Sync + Fn(&mut O) -> Option<&mut T>,
Expand description

Lifts a Fact about some optional subset of data into a Fact about the superset.

In other words, if type O contains a Option<T>, and you have a Fact<T>, PrismFact lets you lift that fact into a Fact<O>.

The prism closure provides an optional mutable view into the subset. If the prism returns None during any fact application, the fact will effectively be skipped for this item: no check or mutation will be performed, and the state will not advance.

A prism is like a lens, except that the target value may or may not exist. It is typically used for enums, or any structure where data may or may not be present.

use contrafact::*;
use arbitrary::{Arbitrary, Unstructured};

#[derive(Debug, Clone, PartialEq, Arbitrary)]
enum E {
    X(u32),
    Y(u32),
}

impl E {
    fn x(&mut self) -> Option<&mut u32> {
        match self {
            E::X(x) => Some(x),
            _ => None,
        }
    }
    fn y(&mut self) -> Option<&mut u32> {
        match self {
            E::Y(y) => Some(y),
            _ => None,
        }
    }
}

let mut fact = prism("E::x", E::x, eq(1));

assert!(fact.clone().check(&E::X(1)).is_ok());
assert!(fact.clone().check(&E::X(2)).is_err());
assert!(fact.clone().check(&E::Y(99)).is_ok());

let mut g = utils::random_generator();
let e = fact.build(&mut g);
match e {
    E::X(x) => assert_eq!(x, 1),
    _ => (),  // Y is not defined by the prism, so it can take on any value.
};

The prism closure is a rather lazy way to provide a prism in the traditional optics sense. We may consider using a true lens library for this in the future.