mirror of
https://github.com/danbulant/mangui
synced 2026-05-19 03:58:34 +00:00
working signal reactivity
This commit is contained in:
parent
d3cc64375a
commit
9b70b0f7bc
3 changed files with 83 additions and 68 deletions
|
|
@ -20,8 +20,7 @@ make_component!(
|
|||
test_: bool = false
|
||||
}
|
||||
Reactive {
|
||||
println!("reactive block");
|
||||
println!("test_ = {}", $test_);
|
||||
dbg!($test_);
|
||||
}
|
||||
Component {
|
||||
@layout {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue