use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
use super::rx_observable::RxObservable;
use super::rx_ref::RxRef;
use super::rx_subject::RxSubject;
use super::tracker::Tracker;
type Subscriber<T> = Rc<RefCell<Box<dyn FnMut(&T)>>>;
struct RxValInner<T> {
value: T,
subscribers: Vec<Subscriber<T>>,
_lifetime_tracker: Option<Rc<dyn Any>>,
}
#[derive(Clone)]
pub struct RxVal<T> {
inner: Rc<RefCell<RxValInner<T>>>,
}
impl<T: 'static> RxVal<T> {
pub fn get(&self) -> T
where
T: Clone,
{
self.inner.borrow().value.clone()
}
pub fn subscribe<F>(&self, tracker: &Tracker, mut f: F)
where
F: FnMut(&T) + 'static,
T: Clone,
{
{
let inner = self.inner.borrow();
f(&inner.value);
}
let subscriber = Rc::new(RefCell::new(Box::new(f) as Box<dyn FnMut(&T)>));
let subscriber_clone = subscriber.clone();
let inner_clone = self.inner.clone();
self.inner.borrow_mut().subscribers.push(subscriber_clone);
tracker.add(move || {
inner_clone
.borrow_mut()
.subscribers
.retain(|s| !Rc::ptr_eq(s, &subscriber));
});
}
pub fn subscriber_count(&self) -> usize {
self.inner.borrow().subscribers.len()
}
pub fn stream(&self) -> RxObservable<T>
where
T: Clone,
{
let subject = RxSubject::new();
let tracker = Rc::new(Tracker::new());
let first = Rc::new(RefCell::new(true));
let subject_clone = subject.clone();
self.subscribe(&tracker, move |value| {
if *first.borrow() {
*first.borrow_mut() = false;
return; }
subject_clone.next(value.clone());
});
let subject_observable = subject.observable();
subject_observable.inner.borrow_mut()._lifetime_tracker = Some(tracker as Rc<dyn Any>);
subject_observable
}
pub fn map<B, F>(&self, f: F) -> RxVal<B>
where
T: Clone,
B: Clone + PartialEq + 'static,
F: Fn(&T) -> B + 'static,
{
let initial = f(&self.get());
let tracker = Rc::new(Tracker::new());
let rx_ref = RxRef::new(initial);
let rx_ref_clone = rx_ref.clone();
self.subscribe(&tracker, move |value| {
rx_ref_clone.set(f(value));
});
let mapped_val = rx_ref.val();
mapped_val.inner.borrow_mut()._lifetime_tracker = Some(tracker as Rc<dyn Any>);
mapped_val
}
pub fn flat_map<B, F>(&self, f: F) -> RxVal<B>
where
T: Clone,
B: Clone + PartialEq + 'static,
F: Fn(&T) -> RxVal<B> + 'static,
{
let initial_inner = f(&self.get());
let initial_value = initial_inner.get();
let result_ref = RxRef::new(initial_value);
let outer_tracker = Rc::new(Tracker::new());
let inner_tracker = Rc::new(RefCell::new(Tracker::new()));
let result_ref_clone = result_ref.clone();
let inner_tracker_clone = inner_tracker.clone();
let f_clone = Rc::new(f);
self.subscribe(&outer_tracker, move |outer_value| {
let new_inner = f_clone(outer_value);
*inner_tracker_clone.borrow_mut() = Tracker::new();
result_ref_clone.set(new_inner.get());
let result_ref_clone2 = result_ref_clone.clone();
new_inner.subscribe(&inner_tracker_clone.borrow(), move |inner_value| {
result_ref_clone2.set(inner_value.clone());
});
});
let result_val = result_ref.val();
let combined_tracker = Rc::new((outer_tracker, inner_tracker));
result_val.inner.borrow_mut()._lifetime_tracker =
Some(combined_tracker as Rc<dyn std::any::Any>);
result_val
}
pub fn flat_map_ref<B, F>(&self, f: F) -> RxVal<B>
where
T: Clone,
B: Clone + PartialEq + 'static,
F: Fn(&T) -> RxRef<B> + 'static,
{
self.flat_map(move |x| f(x).val())
}
pub fn flat_map_observable<B, F>(&self, f: F) -> RxObservable<B>
where
T: Clone,
B: Clone + 'static,
F: Fn(&T) -> RxObservable<B> + 'static,
{
use super::rx_subject::RxSubject;
let subject = RxSubject::new();
let outer_tracker = Rc::new(Tracker::new());
let inner_tracker = Rc::new(RefCell::new(Tracker::new()));
let subject_clone = subject.clone();
let inner_tracker_clone = inner_tracker.clone();
let f_rc = Rc::new(f);
self.subscribe(&outer_tracker, move |outer_value| {
let new_inner = f_rc(outer_value);
*inner_tracker_clone.borrow_mut() = Tracker::new();
let subject_clone2 = subject_clone.clone();
new_inner.subscribe(&inner_tracker_clone.borrow(), move |inner_value| {
subject_clone2.next(inner_value.clone());
});
});
let observable = subject.observable();
let combined_tracker = Rc::new((outer_tracker, inner_tracker));
observable.inner.borrow_mut()._lifetime_tracker = Some(combined_tracker as Rc<dyn Any>);
observable
}
pub fn flat_map_subject<B, F>(&self, f: F) -> RxObservable<B>
where
T: Clone,
B: Clone + 'static,
F: Fn(&T) -> RxSubject<B> + 'static,
{
self.flat_map_observable(move |x| f(x).observable())
}
pub fn zip_val<U>(&self, other: RxVal<U>) -> RxVal<(T, U)>
where
T: Clone + PartialEq,
U: Clone + PartialEq + 'static,
{
let initial = (self.get(), other.get());
let result_ref = RxRef::new(initial);
let tracker1 = Rc::new(Tracker::new());
let tracker2 = Rc::new(Tracker::new());
let result_ref_clone1 = result_ref.clone();
let other_clone1 = other.clone();
self.subscribe(&tracker1, move |self_val| {
result_ref_clone1.set((self_val.clone(), other_clone1.get()));
});
let result_ref_clone2 = result_ref.clone();
let self_clone = self.clone();
other.subscribe(&tracker2, move |other_val| {
result_ref_clone2.set((self_clone.get(), other_val.clone()));
});
let result_val = result_ref.val();
let combined_tracker = Rc::new((tracker1, tracker2));
result_val.inner.borrow_mut()._lifetime_tracker = Some(combined_tracker as Rc<dyn Any>);
result_val
}
pub fn zip_ref<U>(&self, other: RxRef<U>) -> RxVal<(T, U)>
where
T: Clone + PartialEq,
U: Clone + PartialEq + 'static,
{
self.zip_val(other.val())
}
}
impl<T: 'static> RxVal<T>
where
T: Clone,
{
pub(crate) fn new(value: T) -> Self {
Self {
inner: Rc::new(RefCell::new(RxValInner {
value,
subscribers: Vec::new(),
_lifetime_tracker: None,
})),
}
}
pub(crate) fn update(&self, value: T)
where
T: PartialEq,
{
let mut inner = self.inner.borrow_mut();
if inner.value != value {
inner.value = value;
for subscriber in &inner.subscribers {
let mut sub = subscriber.borrow_mut();
sub(&inner.value);
}
}
}
}