use std::rc::Rc;
use gpui::App;
use super::signal::Signal;
type Getter<T> = Rc<dyn Fn(&App) -> T>;
type Setter<T> = Rc<dyn Fn(&mut App, T)>;
pub struct Binding<T> {
get: Getter<T>,
set: Setter<T>,
}
impl<T> Clone for Binding<T> {
fn clone(&self) -> Self {
Binding {
get: self.get.clone(),
set: self.set.clone(),
}
}
}
impl<T: 'static> Binding<T> {
pub fn new(get: impl Fn(&App) -> T + 'static, set: impl Fn(&mut App, T) + 'static) -> Self {
Binding {
get: Rc::new(get),
set: Rc::new(set),
}
}
pub fn get(&self, cx: &App) -> T {
(self.get)(cx)
}
pub fn set(&self, cx: &mut App, value: T) {
(self.set)(cx, value)
}
pub fn map<U: 'static>(
&self,
from: impl Fn(T) -> U + 'static,
into: impl Fn(U) -> T + 'static,
) -> Binding<U> {
let get = self.get.clone();
let set = self.set.clone();
Binding {
get: Rc::new(move |cx| from(get(cx))),
set: Rc::new(move |cx, value| set(cx, into(value))),
}
}
pub fn constant(value: T) -> Self
where
T: Clone,
{
Binding {
get: Rc::new(move |_cx| value.clone()),
set: Rc::new(|_cx, _value| {}),
}
}
}
impl<T: Clone + PartialEq + 'static> Signal<T> {
pub fn binding(&self) -> Binding<T> {
let read = self.clone();
let write = self.clone();
Binding::new(
move |cx| read.get(cx),
move |cx, value| write.set_if_changed(cx, value),
)
}
}
impl<T: 'static> Signal<T> {
pub fn lens<U: Clone + PartialEq + 'static>(
&self,
get: impl Fn(&T) -> U + 'static,
set: impl Fn(&mut T, U) + 'static,
) -> Binding<U> {
let get = Rc::new(get);
let project = get.clone();
let read = self.clone();
let write = self.clone();
Binding::new(
move |cx| project(read.read(cx)),
move |cx, value| {
if get(write.read(cx)) == value {
return;
}
write.update(cx, |slot| set(slot, value));
},
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clone_shares_the_accessors() {
let binding = Binding::new(|_cx| 1i32, |_cx, _value| {});
let clone = binding.clone();
assert_eq!(Rc::strong_count(&binding.get), 2);
assert_eq!(Rc::strong_count(&clone.set), 2);
}
#[test]
fn map_shares_the_source_accessors() {
let binding = Binding::new(|_cx| 1.5f64, |_cx, _value| {});
let _mapped: Binding<String> = binding.map(|v| v.to_string(), |s| s.parse().unwrap_or(0.0));
assert_eq!(Rc::strong_count(&binding.get), 2);
assert_eq!(Rc::strong_count(&binding.set), 2);
}
}