use crate::*;
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>,
{
let label = label.to_string();
lambda("prism", inner_fact, move |g, fact, mut t| {
if let Some(t) = prism(&mut t) {
*t = fact
.mutate(g, t.clone())
.map_check_err(|err| format!("prism({}) > {}", label, err))?;
}
Ok(t)
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils;
use arbitrary::*;
#[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,
}
}
}
#[test]
fn stateless() {
observability::test_run().ok();
let mut g = utils::random_generator();
let f = || {
facts::vec(facts![
prism("E::x", E::x, facts::eq(1)),
prism("E::y", E::y, facts::eq(2)),
])
};
let items = f().build(&mut g);
f().check(&items).unwrap();
assert!(items.iter().all(|e| match e {
E::X(x) => *x == 1,
E::Y(y) => *y == 2,
}))
}
#[test]
fn stateful() {
use itertools::*;
observability::test_run().ok();
let mut g = utils::random_generator();
let f = || {
facts::vec(facts![
prism(
"E::x",
E::x,
facts::consecutive_int("must be increasing", 0),
),
prism(
"E::y",
E::y,
facts::consecutive_int("must be increasing", 0),
),
])
};
let items = f().build(&mut g);
f().check(&items).unwrap();
let (xs, ys): (Vec<_>, Vec<_>) = items.into_iter().partition_map(|e| match e {
E::X(x) => Either::Left(x),
E::Y(y) => Either::Right(y),
});
facts::vec(crate::facts![facts::consecutive_int_(0u32)])
.check(&xs)
.unwrap();
facts::vec(crate::facts![facts::consecutive_int_(0u32)])
.check(&ys)
.unwrap();
}
}