guise/reactive/
binding.rs1use std::rc::Rc;
20
21use gpui::App;
22
23use super::signal::Signal;
24
25type Getter<T> = Rc<dyn Fn(&App) -> T>;
26type Setter<T> = Rc<dyn Fn(&mut App, T)>;
27
28pub struct Binding<T> {
35 get: Getter<T>,
36 set: Setter<T>,
37}
38
39impl<T> Clone for Binding<T> {
40 fn clone(&self) -> Self {
41 Binding {
42 get: self.get.clone(),
43 set: self.set.clone(),
44 }
45 }
46}
47
48impl<T: 'static> Binding<T> {
49 pub fn new(get: impl Fn(&App) -> T + 'static, set: impl Fn(&mut App, T) + 'static) -> Self {
51 Binding {
52 get: Rc::new(get),
53 set: Rc::new(set),
54 }
55 }
56
57 pub fn get(&self, cx: &App) -> T {
59 (self.get)(cx)
60 }
61
62 pub fn set(&self, cx: &mut App, value: T) {
64 (self.set)(cx, value)
65 }
66
67 pub fn map<U: 'static>(
70 &self,
71 from: impl Fn(T) -> U + 'static,
72 into: impl Fn(U) -> T + 'static,
73 ) -> Binding<U> {
74 let get = self.get.clone();
75 let set = self.set.clone();
76 Binding {
77 get: Rc::new(move |cx| from(get(cx))),
78 set: Rc::new(move |cx, value| set(cx, into(value))),
79 }
80 }
81
82 pub fn constant(value: T) -> Self
85 where
86 T: Clone,
87 {
88 Binding {
89 get: Rc::new(move |_cx| value.clone()),
90 set: Rc::new(|_cx, _value| {}),
91 }
92 }
93}
94
95impl<T: Clone + PartialEq + 'static> Signal<T> {
96 pub fn binding(&self) -> Binding<T> {
99 let read = self.clone();
100 let write = self.clone();
101 Binding::new(
102 move |cx| read.get(cx),
103 move |cx, value| write.set_if_changed(cx, value),
104 )
105 }
106}
107
108impl<T: 'static> Signal<T> {
109 pub fn lens<U: Clone + PartialEq + 'static>(
112 &self,
113 get: impl Fn(&T) -> U + 'static,
114 set: impl Fn(&mut T, U) + 'static,
115 ) -> Binding<U> {
116 let get = Rc::new(get);
117 let project = get.clone();
118 let read = self.clone();
119 let write = self.clone();
120 Binding::new(
121 move |cx| project(read.read(cx)),
122 move |cx, value| {
123 if get(write.read(cx)) == value {
124 return;
125 }
126 write.update(cx, |slot| set(slot, value));
127 },
128 )
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn clone_shares_the_accessors() {
138 let binding = Binding::new(|_cx| 1i32, |_cx, _value| {});
139 let clone = binding.clone();
140 assert_eq!(Rc::strong_count(&binding.get), 2);
141 assert_eq!(Rc::strong_count(&clone.set), 2);
142 }
143
144 #[test]
145 fn map_shares_the_source_accessors() {
146 let binding = Binding::new(|_cx| 1.5f64, |_cx, _value| {});
147 let _mapped: Binding<String> = binding.map(|v| v.to_string(), |s| s.parse().unwrap_or(0.0));
148 assert_eq!(Rc::strong_count(&binding.get), 2);
150 assert_eq!(Rc::strong_count(&binding.set), 2);
151 }
152}