Created
May 28, 2023 01:49
-
-
Save valyagolev/22eaad3c015e3722768bab8f8463ee59 to your computer and use it in GitHub Desktop.
bi-directional comm between dioxus and the outside
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{ | |
mem::ManuallyDrop, | |
ops::{Deref, DerefMut}, | |
sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}, | |
}; | |
use dioxus::prelude::Scope; | |
pub struct UiAtomSubscription<T> { | |
atom: Arc<UiAtom<T>>, | |
subscription: Arc<dyn Fn() + Send + Sync>, | |
} | |
impl<T> Drop for UiAtomSubscription<T> { | |
fn drop(&mut self) { | |
self.atom.unsubscribe(&self.subscription) | |
} | |
} | |
pub fn use_ui_atom<T: 'static>(cx: Scope, atom: &Arc<UiAtom<T>>) { | |
cx.use_hook(|| { | |
let update = cx.schedule_update(); | |
atom.subscribe(update.clone()); | |
UiAtomSubscription { | |
atom: atom.clone(), | |
subscription: update, | |
} | |
}); | |
} | |
pub struct UiAtom<T> { | |
value: RwLock<T>, | |
subscribers: Mutex<Vec<Arc<dyn Fn() + Send + Sync>>>, | |
} | |
pub struct UiAtomGuard<'a, T> { | |
value: ManuallyDrop<RwLockWriteGuard<'a, T>>, | |
atom: &'a UiAtom<T>, | |
} | |
impl<T> UiAtom<T> { | |
pub fn new(value: T) -> Self { | |
Self { | |
value: RwLock::new(value), | |
subscribers: Mutex::new(vec![]), | |
} | |
} | |
pub fn read(&self) -> RwLockReadGuard<T> { | |
self.value.read().unwrap() | |
} | |
pub fn write<'a>(&'a self) -> UiAtomGuard<'a, T> { | |
UiAtomGuard { | |
atom: self, | |
value: ManuallyDrop::new(self.value.write().unwrap()), | |
} | |
} | |
pub fn subscribe(&self, subscriber: Arc<dyn Fn() + Send + Sync>) { | |
self.subscribers.lock().unwrap().push(subscriber); | |
} | |
pub fn unsubscribe(&self, subscriber: &Arc<dyn Fn() + Send + Sync>) { | |
let mut subs = self.subscribers.lock().unwrap(); | |
let prev_len = subs.len(); | |
subs.retain(|s| !Arc::ptr_eq(s, subscriber)); | |
assert!(prev_len == subs.len() + 1); | |
} | |
} | |
impl<T> Drop for UiAtomGuard<'_, T> { | |
fn drop(&mut self) { | |
let guard = unsafe { ManuallyDrop::take(&mut self.value) }; | |
drop(guard); | |
for subscriber in self.atom.subscribers.lock().unwrap().iter() { | |
subscriber(); | |
} | |
} | |
} | |
impl<T> Deref for UiAtomGuard<'_, T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
&self.value | |
} | |
} | |
impl<T> DerefMut for UiAtomGuard<'_, T> { | |
fn deref_mut(&mut self) -> &mut T { | |
&mut self.value | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_ui_atom() { | |
let atom = Arc::new(UiAtom::new(0)); | |
println!("1"); | |
assert!(*atom.read() == 0); | |
println!("2"); | |
*atom.write() += 1; | |
assert!(*atom.read() == 1); | |
println!("3"); | |
*atom.write() = 21; | |
assert!(*atom.read() == 21); | |
println!("4"); | |
let s0: Arc<(dyn Fn() + Send + Sync + 'static)> = { | |
let atom = atom.clone(); | |
let a = Mutex::new(0); | |
let s0 = { | |
let atom = atom.clone(); | |
Arc::new(move || { | |
let mut v = a.lock().unwrap(); | |
*v += 1; | |
println!("Sub0: {} value={}", v, atom.read()); | |
}) | |
}; | |
atom.clone().subscribe(s0.clone()); | |
s0 | |
}; | |
println!("5"); | |
assert!(*atom.read() == 21); | |
*atom.write() = 123; | |
assert!(*atom.read() == 123); | |
println!("6"); | |
*atom.write() = 456; | |
println!("7"); | |
assert!(*atom.read() == 456); | |
println!("8"); | |
{ | |
let atom = atom.clone(); | |
let a = Mutex::new(0); | |
atom.clone().subscribe(Arc::new(move || { | |
let mut v = a.lock().unwrap(); | |
*v += 1; | |
println!("Sub1: {} value={}", v, atom.read()); | |
})); | |
} | |
assert!(*atom.read() == 456); | |
*atom.write() = 123; | |
assert!(*atom.read() == 123); | |
println!("16"); | |
*atom.write() = 456; | |
println!("17"); | |
assert!(*atom.read() == 456); | |
println!("18"); | |
atom.unsubscribe(&s0); | |
println!("19"); | |
*atom.write() = 456; | |
println!("20"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment