diff --git a/rusalka/src/lib.rs b/rusalka/src/lib.rs index c48ae35..c21f762 100644 --- a/rusalka/src/lib.rs +++ b/rusalka/src/lib.rs @@ -6,6 +6,7 @@ use mangui::nodes::Node; pub mod component; pub mod nodes; pub mod invalidator; +pub mod store; pub type SharedComponent = Arc>; pub type WeakSharedComponent = std::sync::Weak>; diff --git a/rusalka/src/store/mod.rs b/rusalka/src/store/mod.rs new file mode 100644 index 0000000..d81b2c9 --- /dev/null +++ b/rusalka/src/store/mod.rs @@ -0,0 +1,114 @@ +use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak}; + + +/// Unsubscribes from the store when dropped +pub trait StoreUnsubscribe: Drop {} + +pub trait ReadableStore { + type State; + fn get(&self) -> &Self::State; + fn subscribe(&self, callback: Box ()>) -> Box; +} + +pub trait WritableStore: ReadableStore { + fn set(&mut self, state: Self::State); +} + +struct Listener { + hash: u64, + callback: Box ()> +} + +struct ReadableUnsubscribe { + listeners: Weak>>>, + hash: u64 +} + +impl Drop for ReadableUnsubscribe { + fn drop(&mut self) { + if let Some(listeners) = self.listeners.upgrade() { + let mut listeners = listeners.lock().unwrap(); + listeners.retain(|listener| listener.hash != self.hash); + } + } +} + +impl StoreUnsubscribe for ReadableUnsubscribe {} + +pub struct Readable { + state: T, + listeners: Arc>>> +} + +impl Readable { + pub fn new(state: T) -> Self { + Self { + state, + listeners: Arc::new(Mutex::new(Vec::new())) + } + } +} + +static CALL_COUNT: AtomicU64 = AtomicU64::new(0); + +impl ReadableStore for Readable { + type State = T; + fn get(&self) -> &Self::State { + &self.state + } + fn subscribe(&self, callback: Box ()>) -> Box { + let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let mut listeners = self.listeners.lock().unwrap(); + listeners.push(Listener { + callback, + hash + }); + Box::new(ReadableUnsubscribe { + listeners: Arc::downgrade(&self.listeners), + hash + }) + } +} + +pub struct Writable { + state: T, + listeners: Arc>>> +} + +impl Writable { + pub fn new(state: T) -> Self { + Self { + state, + listeners: Arc::new(Mutex::new(Vec::new())) + } + } +} + +impl ReadableStore for Writable { + type State = T; + fn get(&self) -> &Self::State { + &self.state + } + fn subscribe(&self, callback: Box ()>) -> Box { + let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let mut listeners = self.listeners.lock().unwrap(); + listeners.push(Listener { + callback, + hash + }); + Box::new(ReadableUnsubscribe { + listeners: Arc::downgrade(&self.listeners), + hash + }) + } +} + +impl WritableStore for Writable { + fn set(&mut self, state: Self::State) { + self.state = state; + let mut listeners = self.listeners.lock().unwrap(); + for listener in listeners.iter_mut() { + (listener.callback)(&self.state); + } + } +} \ No newline at end of file