use crate::ScalarLoss;
use crate::{Beam, Optic, Prism};
use std::convert::Infallible;
#[derive(Clone, Copy)]
pub struct Setter<S, A> {
modify_fn: fn(S, &dyn Fn(A) -> A) -> S,
}
impl<S: 'static, A: 'static> Setter<S, A> {
pub fn new(modify: fn(S, &dyn Fn(A) -> A) -> S) -> Self {
Setter { modify_fn: modify }
}
pub fn modify<F>(&self, s: S, f: F) -> S
where
F: Fn(A) -> A + 'static,
{
(self.modify_fn)(s, &f)
}
}
impl<S: Clone + 'static, A: Clone + 'static> Prism for Setter<S, A> {
type Input = Optic<(), S, Infallible, ScalarLoss>;
type Focused = Optic<S, S, Infallible, ScalarLoss>;
type Projected = Optic<S, S, Infallible, ScalarLoss>;
type Refracted = Optic<S, S, Infallible, ScalarLoss>;
fn focus(&self, beam: Self::Input) -> Self::Focused {
let s = beam.result().ok().expect("focus: Err beam").clone();
beam.next(s)
}
fn project(&self, beam: Self::Focused) -> Self::Projected {
let s = beam.result().ok().expect("project: Err beam").clone();
beam.next(s)
}
fn settle(&self, beam: Self::Projected) -> Self::Refracted {
let s = beam.result().ok().expect("settle: Err beam").clone();
let witnessed = (self.modify_fn)(s, &|a| a);
beam.next(witnessed)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Beam as BeamTrait;
#[derive(Clone, Debug, PartialEq)]
struct Box2 {
label: String,
count: i32,
}
fn box2_modify_count(b: Box2, f: &dyn Fn(i32) -> i32) -> Box2 {
Box2 {
count: f(b.count),
..b
}
}
#[test]
fn setter_modifies_field() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "test".to_string(),
count: 5,
};
let b2 = s.modify(b, |c| c + 10);
assert_eq!(b2.count, 15);
assert_eq!(b2.label, "test");
}
#[test]
fn setter_identity_law() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "x".to_string(),
count: 7,
};
let b2 = s.modify(b.clone(), |a| a);
assert_eq!(b2, b);
}
fn seed<T: Clone>(v: T) -> Optic<(), T, Infallible, ScalarLoss> {
Optic::ok((), v)
}
#[test]
fn setter_prism_focus_passes_through() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "x".to_string(),
count: 5,
};
let focused = s.focus(seed(b.clone()));
assert_eq!(focused.result().ok(), Some(&b));
}
#[test]
fn setter_prism_project_passes_through() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "x".to_string(),
count: 5,
};
let focused = s.focus(seed(b.clone()));
let projected = s.project(focused);
assert_eq!(projected.result().ok(), Some(&b));
}
#[test]
fn setter_prism_refract_witnesses_modify() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "x".to_string(),
count: 5,
};
let focused = s.focus(seed(b.clone()));
let projected = s.project(focused);
let refracted = s.settle(projected);
assert_eq!(refracted.result().ok(), Some(&b));
}
#[test]
fn setter_prism_is_lossless() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let b = Box2 {
label: "t".to_string(),
count: 3,
};
let focused = s.focus(seed(b));
assert!(!focused.is_partial());
}
#[test]
fn setter_is_clone_and_copy() {
let s: Setter<Box2, i32> = Setter::new(box2_modify_count);
let s2 = s; let s3 = s2.clone(); let b = Box2 {
label: "t".to_string(),
count: 3,
};
assert_eq!(s3.modify(b, |c| c * 2).count, 6);
}
}