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