working signal reactivity

This commit is contained in:
Daniel Bulant 2024-02-25 23:40:52 +01:00
parent d3cc64375a
commit 9b70b0f7bc
3 changed files with 83 additions and 68 deletions

View file

@ -20,8 +20,7 @@ make_component!(
test_: bool = false test_: bool = false
} }
Reactive { Reactive {
println!("reactive block"); dbg!($test_);
println!("test_ = {}", $test_);
} }
Component { Component {
@layout { @layout {

View file

@ -316,9 +316,9 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
for variable in &reactive_variables { for variable in &reactive_variables {
component_struct_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); component_struct_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
component_struct_stream.extend(TokenStream::from(quote!(:))); component_struct_stream.extend(TokenStream::from(quote!(:)));
component_struct_stream.extend(TokenStream::from(quote!(std::sync::Arc<std::sync::Mutex<rusalka::store::Writable<))); component_struct_stream.extend(TokenStream::from(quote!(std::sync::Arc<rusalka::store::Writable<)));
component_struct_stream.extend(variable.type_.clone()); component_struct_stream.extend(variable.type_.clone());
component_struct_stream.extend(TokenStream::from(quote!(>>>))); component_struct_stream.extend(TokenStream::from(quote!(>>)));
component_struct_stream.extend(TokenStream::from(quote!(,))); component_struct_stream.extend(TokenStream::from(quote!(,)));
} }
@ -380,9 +380,9 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
for attribute in &attributes { for attribute in &attributes {
attributes_default_struct_stream.extend(TokenStream::from(quote!(pub))); attributes_default_struct_stream.extend(TokenStream::from(quote!(pub)));
attributes_default_struct_stream.extend(Some(TokenTree::Ident(attribute.name.clone()))); attributes_default_struct_stream.extend(Some(TokenTree::Ident(attribute.name.clone())));
attributes_default_struct_stream.extend(TokenStream::from(quote!(: std::sync::Arc<std::sync::Mutex<rusalka::store::Writable<))); attributes_default_struct_stream.extend(TokenStream::from(quote!(: std::sync::Arc<rusalka::store::Writable<)));
attributes_default_struct_stream.extend(attribute.type_.clone()); attributes_default_struct_stream.extend(attribute.type_.clone());
attributes_default_struct_stream.extend(TokenStream::from(quote!(>>>,))); attributes_default_struct_stream.extend(TokenStream::from(quote!(>>,)));
} }
output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream)))); output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream))));
@ -450,7 +450,7 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
for attribute in &attributes { for attribute in &attributes {
let name = attribute.name.clone(); let name = attribute.name.clone();
from_fn_stream_inner.extend(TokenStream::from(quote!( from_fn_stream_inner.extend(TokenStream::from(quote!(
#name : std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new(attrs.#name))) #name : std::sync::Arc::new(rusalka::store::Writable::new(attrs.#name))
))); )));
} }
@ -500,8 +500,8 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
invalidator_inner.extend(TokenStream::from(quote!(Default::default()))); invalidator_inner.extend(TokenStream::from(quote!(Default::default())));
} }
new_stream.extend(TokenStream::from(quote!( new_stream.extend(TokenStream::from(quote!(
let #name : std::sync::Arc<std::sync::Mutex<rusalka::store::Writable< #type_ >>> = let #name : std::sync::Arc<rusalka::store::Writable< #type_ >> =
std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new( #invalidator_inner ))); std::sync::Arc::new(rusalka::store::Writable::new( #invalidator_inner ));
))); )));
} }
@ -568,13 +568,19 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
let ident = format_ident!("sub{}", i); let ident = format_ident!("sub{}", i);
let content = &reactive_block.contents; let content = &reactive_block.contents;
let variables = &reactive_block.variables; let variables = &reactive_block.variables;
let vecvariables = reactive_block.variables.iter().map(|v| format_ident!("vec{}", v));
let vecvariables2 = reactive_block.variables.iter().map(|v| format_ident!("vec{}", v));
new_returnvalue_stream.extend(quote!( new_returnvalue_stream.extend(quote!(
#ident: { #ident: {
#(let #variables = #variables.clone();)* #(let #variables = #variables.clone();)*
[#(#variables.clone().lock().unwrap()),*].subscribe(Box::new(move || { #(let #vecvariables = #variables.clone();)*
let vec = [#(#vecvariables2),*];
let res = vec.subscribe(Box::new(move || {
#(let #variables = #variables.clone();)* #(let #variables = #variables.clone();)*
#content #content
})) }));
drop(vec);
res
}, },
)); ));
i+=1; i+=1;
@ -675,7 +681,7 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
set_stream_inner.extend(TokenStream::from(quote!(self.attrs.))); set_stream_inner.extend(TokenStream::from(quote!(self.attrs.)));
set_stream_inner.extend(Some(TokenTree::Ident(attribute.name.clone()))); set_stream_inner.extend(Some(TokenTree::Ident(attribute.name.clone())));
set_stream_inner.extend(TokenStream::from(quote!(.lock().unwrap().set))); set_stream_inner.extend(TokenStream::from(quote!(.set)));
let mut set_stream_inner_inner = TokenStream::new(); let mut set_stream_inner_inner = TokenStream::new();
@ -869,7 +875,7 @@ fn replace_variables(stream: TokenStream) -> (Vec<Ident>, TokenStream) {
idents.push(ident.clone()); idents.push(ident.clone());
output.extend(TokenStream::from(quote!(**))); output.extend(TokenStream::from(quote!(**)));
output.extend(Some(TokenTree::Ident(ident.clone()))); output.extend(Some(TokenTree::Ident(ident.clone())));
output.extend(TokenStream::from(quote!(.lock().unwrap().guard()))); output.extend(TokenStream::from(quote!(.guard())));
}, },
TokenTree::Group(group) => { TokenTree::Group(group) => {
let group_delim = group.delimiter(); let group_delim = group.delimiter();

View file

@ -1,4 +1,5 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::ptr::drop_in_place;
use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak, MutexGuard}; use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak, MutexGuard};
@ -11,11 +12,11 @@ pub trait Signal {
pub trait ReadableStore: Signal { pub trait ReadableStore: Signal {
type State; type State;
fn get(&self) -> &Self::State; fn get(&self) -> MutexGuard<Self::State>;
} }
pub trait WritableStore: ReadableStore { pub trait WritableStore: ReadableStore {
fn set(&mut self, state: Self::State); fn set(&self, state: Self::State);
} }
struct Listener { struct Listener {
@ -38,54 +39,54 @@ impl Drop for ReadableUnsubscribe {
impl StoreUnsubscribe for ReadableUnsubscribe {} impl StoreUnsubscribe for ReadableUnsubscribe {}
pub struct Readable<T> { // this isn't actually usable as of now, so it's commented out until I think of a better way to do it
state: T, // pub struct Readable<T> {
listeners: Arc<Mutex<Vec<Listener>>> // state: T,
} // listeners: Arc<Mutex<Vec<Listener>>>
// }
impl<T> Readable<T> { //
pub fn new(state: T) -> Self { // impl<T> Readable<T> {
Self { // pub fn new(state: T) -> Self {
state, // Self {
listeners: Arc::new(Mutex::new(Vec::new())) // state,
} // listeners: Arc::new(Mutex::new(Vec::new()))
} // }
} // }
// }
static CALL_COUNT: AtomicU64 = AtomicU64::new(0); static CALL_COUNT: AtomicU64 = AtomicU64::new(0);
impl<T: 'static> Signal for Readable<T> { // impl<T: 'static> Signal for Readable<T> {
fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> { // fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); // let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut listeners = self.listeners.lock().unwrap(); // let mut listeners = self.listeners.lock().unwrap();
callback(); // listeners.push(Listener {
listeners.push(Listener { // callback,
callback, // hash
hash // });
}); // Box::new(ReadableUnsubscribe {
Box::new(ReadableUnsubscribe { // listeners: Arc::downgrade(&self.listeners),
listeners: Arc::downgrade(&self.listeners), // hash
hash // })
}) // }
} // }
} //
// impl<T: 'static> ReadableStore for Readable<T> {
impl<T: 'static> ReadableStore for Readable<T> { // type State = T;
type State = T; // fn get(&self) -> &Self::State {
fn get(&self) -> &Self::State { // &self.state
&self.state // }
} // }
}
pub struct Writable<T> { pub struct Writable<T> {
state: T, state: Mutex<T>,
listeners: Arc<Mutex<Vec<Listener>>> listeners: Arc<Mutex<Vec<Listener>>>
} }
impl<T> Writable<T> { impl<T> Writable<T> {
pub fn new(state: T) -> Self { pub fn new(state: T) -> Self {
Self { Self {
state, state: Mutex::new(state),
listeners: Arc::new(Mutex::new(Vec::new())) listeners: Arc::new(Mutex::new(Vec::new()))
} }
} }
@ -95,7 +96,6 @@ impl<T: 'static> Signal for Writable<T> {
fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> { fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut listeners = self.listeners.lock().unwrap(); let mut listeners = self.listeners.lock().unwrap();
callback();
listeners.push(Listener { listeners.push(Listener {
callback, callback,
hash hash
@ -109,14 +109,14 @@ impl<T: 'static> Signal for Writable<T> {
impl<T: 'static> ReadableStore for Writable<T> { impl<T: 'static> ReadableStore for Writable<T> {
type State = T; type State = T;
fn get(&self) -> &Self::State { fn get(&self) -> MutexGuard<Self::State> {
&self.state self.state.lock().unwrap()
} }
} }
impl<T: 'static> WritableStore for Writable<T> { impl<T: 'static> WritableStore for Writable<T> {
fn set(&mut self, state: Self::State) { fn set(&self, state: Self::State) {
self.state = state; *self.state.lock().unwrap().deref_mut() = state;
let mut listeners = self.listeners.lock().unwrap(); let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.iter_mut() { for listener in listeners.iter_mut() {
(listener.callback)(); (listener.callback)();
@ -130,43 +130,48 @@ impl<T: 'static> Default for Writable<T> where T: Default {
} }
} }
pub struct DerefGuard<T> { pub struct DerefGuard<'a, T> {
listeners: Arc<Mutex<Vec<Listener>>>, listeners: Arc<Mutex<Vec<Listener>>>,
tainted: bool, tainted: bool,
inner: T // this is an option because we need to drop the guard before we trigger the listeners
// this is only none during the drop function - see https://doc.rust-lang.org/stable/nomicon/destructors.html
inner: Option<MutexGuard<'a, T>>
} }
pub trait DerefGuardExt<T>: WritableStore + Signal { pub trait DerefGuardExt<T>: WritableStore + Signal {
fn guard(&mut self) -> DerefGuard<&mut T>; fn guard(&self) -> DerefGuard<T>;
} }
impl <T: 'static> DerefGuardExt<T> for Writable<T> { impl <T: 'static> DerefGuardExt<T> for Writable<T> {
fn guard(&mut self) -> DerefGuard<&mut T> { fn guard(&self) -> DerefGuard<T> {
DerefGuard { DerefGuard {
listeners: self.listeners.clone(), listeners: self.listeners.clone(),
tainted: false, tainted: false,
inner: &mut self.state inner: Some(self.state.lock().unwrap())
} }
} }
} }
impl<T> Deref for DerefGuard<T> { impl<'a, T> Deref for DerefGuard<'a, T> {
type Target = T; type Target = MutexGuard<'a, T>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner self.inner.as_ref().unwrap()
} }
} }
impl<T> DerefMut for DerefGuard<T> { impl<'a, T> DerefMut for DerefGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.tainted = true; self.tainted = true;
&mut self.inner self.inner.as_mut().unwrap()
} }
} }
impl<T> Drop for DerefGuard<T> { impl<'a, T> Drop for DerefGuard<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
if self.tainted { if self.tainted {
let inner = self.inner.take().unwrap();
drop(inner);
let mut listeners = self.listeners.lock().unwrap(); let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.iter_mut() { for listener in listeners.iter_mut() {
(listener.callback)(); (listener.callback)();
@ -190,6 +195,11 @@ impl<T: Signal> Signal for MutexGuard<'_, T> {
self.deref().subscribe(callback) self.deref().subscribe(callback)
} }
} }
impl<T: Signal> Signal for Arc<T> {
fn subscribe(&self, callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
self.deref().subscribe(callback)
}
}
impl<T: Signal> Signal for Vec<T> { impl<T: Signal> Signal for Vec<T> {
/// Subscribes to all signals in the vector /// Subscribes to all signals in the vector