1use crate::optic::Optic;
2
3pub struct Setter<S, T, A, B> {
8 #[allow(clippy::type_complexity)]
9 modify: Box<dyn Fn(S, &dyn Fn(A) -> B) -> T>,
10}
11
12pub type SimpleSetter<S, A> = Setter<S, S, A, A>;
14
15impl<S, T, A, B> Optic for Setter<S, T, A, B> {}
16
17impl<S, T, A, B> Setter<S, T, A, B> {
18 pub fn new(modify: impl Fn(S, &dyn Fn(A) -> B) -> T + 'static) -> Self {
19 Self {
20 modify: Box::new(modify),
21 }
22 }
23
24 pub fn over(&self, s: S, f: impl Fn(A) -> B) -> T {
26 (self.modify)(s, &f)
27 }
28
29 pub fn set(&self, s: S, b: B) -> T
31 where
32 B: Clone,
33 {
34 (self.modify)(s, &|_| b.clone())
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[derive(Debug, Clone, PartialEq)]
43 struct Point {
44 x: i32,
45 y: i32,
46 }
47
48 fn point_x_setter() -> SimpleSetter<Point, i32> {
49 Setter::new(|p: Point, f: &dyn Fn(i32) -> i32| Point { x: f(p.x), ..p })
50 }
51
52 #[test]
53 fn setter_over() {
54 let setter = point_x_setter();
55 let p = Point { x: 1, y: 2 };
56 let result = setter.over(p, |x| x + 10);
57 assert_eq!(result, Point { x: 11, y: 2 });
58 }
59
60 #[test]
61 fn setter_set() {
62 let setter = point_x_setter();
63 let p = Point { x: 1, y: 2 };
64 let result = setter.set(p, 99);
65 assert_eq!(result, Point { x: 99, y: 2 });
66 }
67
68 #[test]
69 fn setter_identity_law() {
70 let setter = point_x_setter();
72 let p = Point { x: 5, y: 10 };
73 let result = setter.over(p.clone(), |x| x);
74 assert_eq!(result, p);
75 }
76
77 #[test]
78 fn setter_from_lens() {
79 use crate::lens::Lens;
80 let lens = Lens::new(|p: &Point| p.x, |p: Point, x| Point { x, ..p });
81 let setter = lens.to_setter();
82 let p = Point { x: 1, y: 2 };
83 let result = setter.over(p, |x| x + 10);
84 assert_eq!(result, Point { x: 11, y: 2 });
85 }
86
87 #[test]
88 fn setter_from_prism() {
89 use crate::prism::Prism;
90
91 #[derive(Debug, Clone, PartialEq)]
92 enum Val {
93 Int(i32),
94 Str(String),
95 }
96 let prism = Prism::new(
97 |v: Val| match v {
98 Val::Int(n) => Ok(n),
99 Val::Str(s) => Err(Val::Str(s)),
100 },
101 Val::Int,
102 );
103 let setter = prism.to_setter();
104 assert_eq!(setter.over(Val::Int(5), |x| x * 2), Val::Int(10));
105 assert_eq!(
106 setter.over(Val::Str("hi".into()), |x| x * 2),
107 Val::Str("hi".into())
108 );
109 }
110}