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
}
Reactive {
println!("reactive block");
println!("test_ = {}", $test_);
dbg!($test_);
}
Component {
@layout {

View file

@ -316,9 +316,9 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
for variable in &reactive_variables {
component_struct_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
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(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 {
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(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(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))));
@ -450,7 +450,7 @@ pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream
for attribute in &attributes {
let name = attribute.name.clone();
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())));
}
new_stream.extend(TokenStream::from(quote!(
let #name : std::sync::Arc<std::sync::Mutex<rusalka::store::Writable< #type_ >>> =
std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new( #invalidator_inner )));
let #name : std::sync::Arc<rusalka::store::Writable< #type_ >> =
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 content = &reactive_block.contents;
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!(
#ident: {
#(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();)*
#content
}))
}));
drop(vec);
res
},
));
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(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();
@ -869,7 +875,7 @@ fn replace_variables(stream: TokenStream) -> (Vec<Ident>, TokenStream) {
idents.push(ident.clone());
output.extend(TokenStream::from(quote!(**)));
output.extend(Some(TokenTree::Ident(ident.clone())));
output.extend(TokenStream::from(quote!(.lock().unwrap().guard())));
output.extend(TokenStream::from(quote!(.guard())));
},
TokenTree::Group(group) => {
let group_delim = group.delimiter();

View file

@ -1,4 +1,5 @@
use std::ops::{Deref, DerefMut};
use std::ptr::drop_in_place;
use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak, MutexGuard};
@ -11,11 +12,11 @@ pub trait Signal {
pub trait ReadableStore: Signal {
type State;
fn get(&self) -> &Self::State;
fn get(&self) -> MutexGuard<Self::State>;
}
pub trait WritableStore: ReadableStore {
fn set(&mut self, state: Self::State);
fn set(&self, state: Self::State);
}
struct Listener {
@ -38,54 +39,54 @@ impl Drop for ReadableUnsubscribe {
impl StoreUnsubscribe for ReadableUnsubscribe {}
pub struct Readable<T> {
state: T,
listeners: Arc<Mutex<Vec<Listener>>>
}
impl<T> Readable<T> {
pub fn new(state: T) -> Self {
Self {
state,
listeners: Arc::new(Mutex::new(Vec::new()))
}
}
}
// this isn't actually usable as of now, so it's commented out until I think of a better way to do it
// pub struct Readable<T> {
// state: T,
// listeners: Arc<Mutex<Vec<Listener>>>
// }
//
// impl<T> Readable<T> {
// pub fn new(state: T) -> Self {
// Self {
// state,
// listeners: Arc::new(Mutex::new(Vec::new()))
// }
// }
// }
static CALL_COUNT: AtomicU64 = AtomicU64::new(0);
impl<T: 'static> Signal for Readable<T> {
fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut listeners = self.listeners.lock().unwrap();
callback();
listeners.push(Listener {
callback,
hash
});
Box::new(ReadableUnsubscribe {
listeners: Arc::downgrade(&self.listeners),
hash
})
}
}
impl<T: 'static> ReadableStore for Readable<T> {
type State = T;
fn get(&self) -> &Self::State {
&self.state
}
}
// impl<T: 'static> Signal for Readable<T> {
// fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
// 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<T: 'static> ReadableStore for Readable<T> {
// type State = T;
// fn get(&self) -> &Self::State {
// &self.state
// }
// }
pub struct Writable<T> {
state: T,
state: Mutex<T>,
listeners: Arc<Mutex<Vec<Listener>>>
}
impl<T> Writable<T> {
pub fn new(state: T) -> Self {
Self {
state,
state: Mutex::new(state),
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> {
let hash = CALL_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let mut listeners = self.listeners.lock().unwrap();
callback();
listeners.push(Listener {
callback,
hash
@ -109,14 +109,14 @@ impl<T: 'static> Signal for Writable<T> {
impl<T: 'static> ReadableStore for Writable<T> {
type State = T;
fn get(&self) -> &Self::State {
&self.state
fn get(&self) -> MutexGuard<Self::State> {
self.state.lock().unwrap()
}
}
impl<T: 'static> WritableStore for Writable<T> {
fn set(&mut self, state: Self::State) {
self.state = state;
fn set(&self, state: Self::State) {
*self.state.lock().unwrap().deref_mut() = state;
let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.iter_mut() {
(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>>>,
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 {
fn guard(&mut self) -> DerefGuard<&mut T>;
fn guard(&self) -> DerefGuard<T>;
}
impl <T: 'static> DerefGuardExt<T> for Writable<T> {
fn guard(&mut self) -> DerefGuard<&mut T> {
fn guard(&self) -> DerefGuard<T> {
DerefGuard {
listeners: self.listeners.clone(),
tainted: false,
inner: &mut self.state
inner: Some(self.state.lock().unwrap())
}
}
}
impl<T> Deref for DerefGuard<T> {
type Target = T;
impl<'a, T> Deref for DerefGuard<'a, T> {
type Target = MutexGuard<'a, T>;
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 {
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) {
if self.tainted {
let inner = self.inner.take().unwrap();
drop(inner);
let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.iter_mut() {
(listener.callback)();
@ -190,6 +195,11 @@ impl<T: Signal> Signal for MutexGuard<'_, T> {
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> {
/// Subscribes to all signals in the vector