switch to signals, start working on slots

This commit is contained in:
Daniel Bulant 2024-02-25 21:33:01 +01:00
parent 455fad5269
commit d3cc64375a
12 changed files with 619 additions and 516 deletions

85
Cargo.lock generated
View file

@ -163,9 +163,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.15.0" version = "3.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
@ -213,11 +213,10 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
dependencies = [ dependencies = [
"jobserver",
"libc", "libc",
] ]
@ -858,9 +857,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.6" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
[[package]] [[package]]
name = "http" name = "http"
@ -1022,15 +1021,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "jpeg-decoder" name = "jpeg-decoder"
version = "0.3.1" version = "0.3.1"
@ -1124,9 +1114,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]] [[package]]
name = "lru" name = "lru"
version = "0.12.2" version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
[[package]] [[package]]
name = "mangades" name = "mangades"
@ -1362,9 +1352,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.100" version = "0.9.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1" checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@ -1640,6 +1630,7 @@ dependencies = [
name = "rusalka-macro" name = "rusalka-macro"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2",
"quote", "quote",
] ]
@ -1906,12 +1897,12 @@ dependencies = [
[[package]] [[package]]
name = "socket2" name = "socket2"
version = "0.5.5" version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -2558,7 +2549,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [ dependencies = [
"windows-targets 0.52.0", "windows-targets 0.52.3",
] ]
[[package]] [[package]]
@ -2593,17 +2584,17 @@ dependencies = [
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm 0.52.0", "windows_aarch64_gnullvm 0.52.3",
"windows_aarch64_msvc 0.52.0", "windows_aarch64_msvc 0.52.3",
"windows_i686_gnu 0.52.0", "windows_i686_gnu 0.52.3",
"windows_i686_msvc 0.52.0", "windows_i686_msvc 0.52.3",
"windows_x86_64_gnu 0.52.0", "windows_x86_64_gnu 0.52.3",
"windows_x86_64_gnullvm 0.52.0", "windows_x86_64_gnullvm 0.52.3",
"windows_x86_64_msvc 0.52.0", "windows_x86_64_msvc 0.52.3",
] ]
[[package]] [[package]]
@ -2620,9 +2611,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -2638,9 +2629,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -2656,9 +2647,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -2674,9 +2665,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -2692,9 +2683,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -2710,9 +2701,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -2728,9 +2719,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
[[package]] [[package]]
name = "winit" name = "winit"

View file

@ -5,12 +5,9 @@ use mangui::nodes::TaffyStyle;
use mangui::taffy::Display::Block; use mangui::taffy::Display::Block;
use mangui::taffy::{FlexDirection, LengthPercentage, Rect}; use mangui::taffy::{FlexDirection, LengthPercentage, Rect};
use rusalka::nodes::primitives::{Rectangle, RectangleAttributes, PartialRectangleAttributes};
make_component!( make_component!(
ComponentDemo, ComponentDemo,
MainLogic { MainLogic {
let _radius = attrs.radius;
let imgpath = std::path::PathBuf::from("./demo/large/bx117324-97mHyfJGwpBq.jpg"); let imgpath = std::path::PathBuf::from("./demo/large/bx117324-97mHyfJGwpBq.jpg");
let imgflags = ImageFlags::empty(); let imgflags = ImageFlags::empty();
let width = 230.; let width = 230.;
@ -23,13 +20,11 @@ make_component!(
test_: bool = false test_: bool = false
} }
Reactive { Reactive {
// dbg!($test_); println!("reactive block");
println!("test_ = {}", $test_);
} }
Component { Component {
@layout { @layout {
@Rectangle {
radius: if $test_ { attrs.radius } else { 0. }
}
@layout { @layout {
@text { @text {
text: String::from("Hello, World 🌎! And there's more text in here, as a single line"), text: String::from("Hello, World 🌎! And there's more text in here, as a single line"),

View file

@ -4,6 +4,7 @@ use mangui::{self, nodes::layout::Layout, SharedNode, MainEntry};
mod component_demo_syntax; mod component_demo_syntax;
mod anilist; mod anilist;
mod slot_demo;
use rusalka::component::Component; use rusalka::component::Component;

266
mangades/src/slot_demo.rs Normal file
View file

@ -0,0 +1,266 @@
use std::sync::{Arc, Mutex, RwLock};
use mangui::nodes::layout::Layout;
use mangui::nodes::primitives::Rectangle;
use rusalka::component::Slot;
use rusalka::store::{DerefGuardExt, ReadableStore, Signal, StoreUnsubscribe, Writable, WritableStore};
pub struct SlotAcceptDemo {
comp0: rusalka::SharedNodeComponent<Layout>,
comp1: Mutex<Option<Slot>>,
selfref: rusalka::WeakSharedComponent<Self>,
attrs: ReactiveSlotAcceptDemoAttributes,
}
type SlotArgs = ();
type DSlot = Option<Mutex<Box<dyn FnMut(SlotArgs) -> Slot>>>;
#[derive(Default)]
pub struct SlotAcceptDemoAttributes {
pub __default_slot: DSlot
}
pub struct ReactiveSlotAcceptDemoAttributes {
__default_slot: DSlot
}
#[derive(Default)]
pub struct PartialSlotAcceptDemoAttributes {
pub __default_slot: Option<DSlot>
}
impl From<SlotAcceptDemoAttributes> for PartialSlotAcceptDemoAttributes {
fn from(attrs: SlotAcceptDemoAttributes) -> Self {
Self { __default_slot: Some(attrs.__default_slot) }
}
}
impl From<SlotAcceptDemoAttributes> for ReactiveSlotAcceptDemoAttributes {
fn from(attrs: SlotAcceptDemoAttributes) -> Self {
Self { __default_slot: attrs.__default_slot }
}
}
impl rusalka::component::Component for SlotAcceptDemo {
type ComponentAttrs = SlotAcceptDemoAttributes;
type ReactiveComponentAttrs = ReactiveSlotAcceptDemoAttributes;
type PartialComponentAttrs = PartialSlotAcceptDemoAttributes;
const UPDATE_LENGTH: usize = 1;
fn new(
attrs: Self::ComponentAttrs,
selfref: rusalka::WeakSharedComponent<Self>,
) -> Self {
let this = Self {
comp0: std::sync::Arc::new(
std::sync::RwLock::new(Layout { ..Default::default() }),
),
comp1: Mutex::new(None),
attrs: attrs.into(),
selfref
};
this
}
fn set(&mut self, attrs: Self::PartialComponentAttrs) {
}
fn get(&self) -> &Self::ReactiveComponentAttrs {
&self.attrs
}
fn mount(
&self,
parent: &mangui::SharedNode,
before: Option<&mangui::SharedNode>,
) {
rusalka::nodes::insert(parent, &{ self.comp0.clone() }, before);
match &self.attrs.__default_slot {
Some(slot) => {
*self.comp1.lock().unwrap() = Some(slot.lock().unwrap()(()));
(*self.comp1.lock().unwrap().as_mut().unwrap().mount)(parent, before);
}
None => {}
}
}
fn unmount(&self) {
rusalka::nodes::detach(&{ self.comp0.clone() });
match &self.attrs.__default_slot {
Some(slot) => {
(*self.comp1.lock().unwrap().as_mut().unwrap().unmount)();
*self.comp1.lock().unwrap() = None;
}
None => {}
}
}
}
struct SlotDemoSlot1 {
comp0: rusalka::SharedNodeComponent<Rectangle>,
sub0: Box<dyn StoreUnsubscribe>
}
pub struct SlotDemo {
comp0: rusalka::SharedNodeComponent<Layout>,
comp1: rusalka::SharedComponent<SlotAcceptDemo>,
test_: std::sync::Arc<std::sync::Mutex<rusalka::store::Writable<bool>>>,
sub0: Box<dyn StoreUnsubscribe>,
selfref: rusalka::WeakSharedComponent<Self>,
attrs: ReactiveSlotDemoAttributes,
}
#[derive(Default)]
pub struct SlotDemoAttributes {
pub test: f32
}
pub struct ReactiveSlotDemoAttributes {
pub test: Arc<Mutex<Writable<f32>>>
}
#[derive(Default)]
pub struct PartialSlotDemoAttributes {
pub test: Option<f32>
}
impl From<SlotDemoAttributes> for PartialSlotDemoAttributes {
fn from(attrs: SlotDemoAttributes) -> Self {
Self { test: Some(attrs.test) }
}
}
impl From<ReactiveSlotDemoAttributes> for SlotDemoAttributes {
fn from(attrs: ReactiveSlotDemoAttributes) -> Self {
Self { test: *attrs.test.lock().unwrap().get() }
}
}
impl From<&ReactiveSlotDemoAttributes> for SlotDemoAttributes {
fn from(attrs: &ReactiveSlotDemoAttributes) -> Self {
Self { test: *attrs.test.lock().unwrap().get() }
}
}
impl From<SlotDemoAttributes> for ReactiveSlotDemoAttributes {
fn from(attrs: SlotDemoAttributes) -> Self {
Self { test: Arc::new(Mutex::new(Writable::new(attrs.test))) }
}
}
impl rusalka::component::Component for SlotDemo {
type ComponentAttrs = SlotDemoAttributes;
type ReactiveComponentAttrs = ReactiveSlotDemoAttributes;
type PartialComponentAttrs = PartialSlotDemoAttributes;
const UPDATE_LENGTH: usize = 1;
fn new(
attrs: Self::ComponentAttrs,
selfref: rusalka::WeakSharedComponent<Self>,
) -> Self {
let attrs: Self::ReactiveComponentAttrs = attrs.into();
let test_: std::sync::Arc<
std::sync::Mutex<rusalka::store::Writable<bool>>,
> = std::sync::Arc::new(
std::sync::Mutex::new(rusalka::store::Writable::new(false)),
);
let test = attrs.test.clone();
let this = Self {
comp0: std::sync::Arc::new(
std::sync::RwLock::new(Layout { ..Default::default() }),
),
comp1: std::sync::Arc::new_cyclic(|cselfref2| std::sync::Mutex::new(
SlotAcceptDemo::new(
SlotAcceptDemoAttributes {
__default_slot: Some(Mutex::new(Box::new(move |_| {
let comp0: Arc<Mutex<Option<SlotDemoSlot1>>> = Arc::new(Mutex::new(None));
Slot {
mount: {
let comp0 = comp0.clone();
let test = test.clone();
Box::new(move |parent, before| {
if let None = comp0.lock().unwrap().as_ref() {
let slot = Some(
SlotDemoSlot1 {
comp0: std::sync::Arc::new(
std::sync::RwLock::new(Rectangle { radius: *test.clone().lock().unwrap().get(), ..Default::default() }),
),
sub0: {
let comp0 = comp0.clone();
let test = test.clone();
[test.clone().lock().unwrap()].subscribe(Box::new(move || {
let comp1 = comp0.clone();
let test1 = test.clone();
let mut comp1l = comp1.lock().unwrap();
if let Some(comp1) = comp1l.as_mut() {
comp1.comp0.write().unwrap().radius = *test1.lock().unwrap().get();
}
}))
},
}
);
*comp0.lock().unwrap() = slot;
}
rusalka::nodes::insert(parent, &{ comp0.lock().as_ref().unwrap().as_ref().unwrap().comp0.clone() }, before);
})
},
unmount: {
let comp0 = comp0.clone();
Box::new(move || {
let comp0 = comp0.clone();
if let Some(comp0) = comp0.lock().unwrap().as_mut() {
rusalka::nodes::detach(&{ comp0.comp0.clone() });
}
*comp0.lock().unwrap() = None;
})
},
}
})))
},
cselfref2.clone(),
),
)),
sub0: {
let test = test_.clone();
[test.clone().lock().unwrap()].subscribe(Box::new(move || {
let test = test.clone();
dbg!(test.lock().unwrap().get());
}))
},
attrs,
selfref,
test_
};
let selfref = this.selfref.clone();
this.comp0
.write()
.unwrap()
.events
.add_handler(
Box::new(move |event| {
let selfref = selfref.upgrade().unwrap();
let mut this = selfref.lock().unwrap();
let attrs = &this.attrs;
let test_ = &this.test_;
match event.event {
mangui::events::InnerEvent::MouseDown(_) => {
**test_.lock().unwrap().guard() = true;
}
mangui::events::InnerEvent::MouseUp(_) => {
**test_.lock().unwrap().guard() = false;
}
_ => {}
}
}),
);
this
}
fn set(&mut self, attrs: Self::PartialComponentAttrs) {
if let Some(test) = attrs.test {
**self.attrs.test.lock().unwrap().guard() = test;
// self.attrs.test.lock().unwrap().set(test);
}
}
fn get(&self) -> &Self::ReactiveComponentAttrs {
&self.attrs
}
fn mount(
&self,
parent: &mangui::SharedNode,
before: Option<&mangui::SharedNode>,
) {
rusalka::nodes::insert(parent, &{ self.comp0.clone() }, before);
}
fn unmount(&self) {
rusalka::nodes::detach(&{ self.comp0.clone() });
}
}

View file

@ -8,6 +8,7 @@ authors.workspace = true
[dependencies] [dependencies]
quote = "1.0" quote = "1.0"
proc-macro2 = "1.0.78"
[lib] [lib]
proc-macro = true proc-macro = true

View file

@ -1,7 +1,7 @@
use proc_macro2::{Ident, TokenStream, TokenTree, Span, Group, Delimiter};
use std::collections::HashMap; use std::collections::HashMap;
use proc_macro::{TokenStream, TokenTree, Ident, Group, Span, Literal}; use quote::{format_ident, quote, ToTokens};
use quote::quote;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Attribute { struct Attribute {
@ -51,7 +51,8 @@ struct ReactiveBlock {
#[proc_macro] #[proc_macro]
/// If you have syntax errors because of attributes, wrap the default value in parentheses. /// If you have syntax errors because of attributes, wrap the default value in parentheses.
pub fn make_component(item: TokenStream) -> TokenStream { pub fn make_component(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item = TokenStream::from(item);
let mut last_identifier = None; let mut last_identifier = None;
let mut item = item.into_iter(); let mut item = item.into_iter();
let name = item.next().unwrap(); let name = item.next().unwrap();
@ -201,11 +202,13 @@ pub fn make_component(item: TokenStream) -> TokenStream {
}, },
"Reactive" => { "Reactive" => {
let (variables, contents) = replace_variables(group.stream()); let (variables, contents) = replace_variables(group.stream());
reactive_blocks.push(ReactiveBlock { if !contents.is_empty() && !variables.is_empty() {
variables, reactive_blocks.push(ReactiveBlock {
contents, variables,
prop_ident: None contents,
}); prop_ident: None
});
}
}, },
"Component" => { "Component" => {
// Example syntax: // Example syntax:
@ -268,25 +271,28 @@ pub fn make_component(item: TokenStream) -> TokenStream {
let mut output = TokenStream::new(); let mut output = TokenStream::new();
// Component struct // Component struct
output.extend(quote!(
use rusalka::store::DerefGuardExt;
use rusalka::store::WritableStore;
use rusalka::store::Signal;
));
output.extend(TokenStream::from(quote!(pub struct))); output.extend(quote!(pub struct));
output.extend(Some(TokenTree::Ident(name_ident.clone()))); output.extend(Some(TokenTree::Ident(name_ident.clone())));
let mut component_struct_stream = TokenStream::new(); let mut component_struct_stream = TokenStream::new();
let mut i = 0; for (i, component) in components_used.iter().enumerate() {
for component in &components_used {
let ident = Ident::new(&format!("comp{}", i), component.name.span()); let ident = Ident::new(&format!("comp{}", i), component.name.span());
let mut midstream = TokenStream::from(quote!(: rusalka::)); let mut midstream = quote!(: rusalka::);
match component.component_type { match component.component_type {
ComponentType::RealComponent => { ComponentType::RealComponent => {
midstream.extend(TokenStream::from(quote!(SharedComponent<))); midstream.extend(quote!(SharedComponent<));
}, },
ComponentType::Node => { ComponentType::Node => {
midstream.extend(TokenStream::from(quote!(SharedNodeComponent<))); midstream.extend(quote!(SharedNodeComponent<));
} }
} }
@ -295,15 +301,22 @@ pub fn make_component(item: TokenStream) -> TokenStream {
component_struct_stream.extend(Some(TokenTree::Ident(ident))); component_struct_stream.extend(Some(TokenTree::Ident(ident)));
component_struct_stream.extend(midstream); component_struct_stream.extend(midstream);
component_struct_stream.extend(Some(TokenTree::Ident(component_name))); component_struct_stream.extend(Some(TokenTree::Ident(component_name)));
component_struct_stream.extend(TokenStream::from(quote!(>,))); component_struct_stream.extend(quote!(>,));
}
let mut i = 0u32;
for block in &reactive_blocks {
let ident = format_ident!("sub{}", i);
component_struct_stream.extend(quote!(
#ident: Box<dyn rusalka::store::StoreUnsubscribe>,
));
i+=1; i+=1;
} }
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::invalidator::Invalidator<))); component_struct_stream.extend(TokenStream::from(quote!(std::sync::Arc<std::sync::Mutex<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!(,)));
@ -316,11 +329,13 @@ pub fn make_component(item: TokenStream) -> TokenStream {
// Attributes struct // Attributes struct
let attributes_ident = Ident::new(&format!("{str_name}Attributes"), name_ident.span()); let attributes_ident = Ident::new(&format!("{str_name}Attributes"), name_ident.span());
let partial_attributes_ident = Ident::new(&format!("Partial{str_name}Attributes"), name_ident.span());
let reactive_attributes_ident = Ident::new(&format!("Reactive{str_name}Attributes"), name_ident.span());
component_struct_stream.extend(TokenStream::from(quote!(attrs: ))); component_struct_stream.extend(TokenStream::from(quote!(attrs: )));
component_struct_stream.extend(Some(TokenTree::Ident(attributes_ident.clone()))); component_struct_stream.extend(Some(TokenTree::Ident(reactive_attributes_ident.clone())));
let component_struct_group = Group::new(proc_macro::Delimiter::Brace, component_struct_stream); let component_struct_group = Group::new(proc_macro2::Delimiter::Brace, component_struct_stream);
output.extend(Some(TokenTree::Group(component_struct_group))); output.extend(Some(TokenTree::Group(component_struct_group)));
@ -338,11 +353,10 @@ pub fn make_component(item: TokenStream) -> TokenStream {
attributes_struct_stream.extend(TokenStream::from(quote!(,))); attributes_struct_stream.extend(TokenStream::from(quote!(,)));
} }
output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, attributes_struct_stream)))); output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_struct_stream))));
// partial attributes // partial attributes
let partial_attributes_ident = Ident::new(&format!("Partial{str_name}Attributes"), name_ident.span());
output.extend(TokenStream::from(quote!(#[derive(Default)] pub struct))); output.extend(TokenStream::from(quote!(#[derive(Default)] pub struct)));
output.extend(Some(TokenTree::Ident(partial_attributes_ident.clone()))); output.extend(Some(TokenTree::Ident(partial_attributes_ident.clone())));
let mut attributes_default_struct_stream = TokenStream::new(); let mut attributes_default_struct_stream = TokenStream::new();
@ -355,7 +369,23 @@ pub fn make_component(item: TokenStream) -> TokenStream {
attributes_default_struct_stream.extend(TokenStream::from(quote!(>,))); attributes_default_struct_stream.extend(TokenStream::from(quote!(>,)));
} }
output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, attributes_default_struct_stream)))); output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream))));
// reactive attributes
output.extend(TokenStream::from(quote!(#[derive(Default)] pub struct)));
output.extend(Some(TokenTree::Ident(reactive_attributes_ident.clone())));
let mut attributes_default_struct_stream = TokenStream::new();
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(attribute.type_.clone());
attributes_default_struct_stream.extend(TokenStream::from(quote!(>>>,)));
}
output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream))));
// impl From<Attributes> for PartialAttributes // impl From<Attributes> for PartialAttributes
@ -371,7 +401,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
let mut from_args = TokenStream::new(); let mut from_args = TokenStream::new();
from_args.extend(TokenStream::from(quote!(attrs:))); from_args.extend(TokenStream::from(quote!(attrs:)));
from_args.extend(Some(TokenTree::Ident(attributes_ident.clone()))); from_args.extend(Some(TokenTree::Ident(attributes_ident.clone())));
from_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, from_args)))); from_stream.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Parenthesis, from_args))));
from_stream.extend(TokenStream::from(quote!(-> Self))); from_stream.extend(TokenStream::from(quote!(-> Self)));
let mut from_fn_stream = TokenStream::from(quote!(Self)); let mut from_fn_stream = TokenStream::from(quote!(Self));
@ -388,57 +418,91 @@ pub fn make_component(item: TokenStream) -> TokenStream {
from_fn_stream_inner_inner.extend(TokenStream::from(quote!(attrs.))); from_fn_stream_inner_inner.extend(TokenStream::from(quote!(attrs.)));
from_fn_stream_inner_inner.extend(Some(TokenTree::Ident(attribute.name.clone()))); from_fn_stream_inner_inner.extend(Some(TokenTree::Ident(attribute.name.clone())));
from_fn_stream_inner.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, from_fn_stream_inner_inner)))); from_fn_stream_inner.extend(Some(TokenTree::Group(Group::new(Delimiter::Parenthesis, from_fn_stream_inner_inner))));
} }
from_fn_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, from_fn_stream_inner)))); from_fn_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_fn_stream_inner))));
from_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, from_fn_stream)))); from_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_fn_stream))));
output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, from_stream)))); output.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_stream))));
// impl From<Attributes> for ReactiveAttributes
output.extend(TokenStream::from(quote!(impl From<)));
output.extend(Some(TokenTree::Ident(attributes_ident.clone())));
output.extend(TokenStream::from(quote!(> for)));
output.extend(Some(TokenTree::Ident(reactive_attributes_ident.clone())));
let mut from_stream = TokenStream::new();
from_stream.extend(TokenStream::from(quote!(fn from)));
let mut from_args = TokenStream::new();
from_args.extend(TokenStream::from(quote!(attrs:)));
from_args.extend(Some(TokenTree::Ident(attributes_ident.clone())));
from_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Parenthesis, from_args))));
from_stream.extend(TokenStream::from(quote!(-> Self)));
let mut from_fn_stream = TokenStream::from(quote!(Self));
let mut from_fn_stream_inner = TokenStream::new();
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)))
)));
}
from_fn_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_fn_stream_inner))));
from_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_fn_stream))));
output.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_stream))));
// Component impl // Component impl
output.extend(TokenStream::from(quote!(impl rusalka::component::Component for))); output.extend(TokenStream::from(quote!(impl rusalka::component::Component for #name_ident)));
output.extend(Some(TokenTree::Ident(name_ident.clone())));
let mut component_impl_stream = TokenStream::new(); let mut component_impl_stream = TokenStream::new();
component_impl_stream.extend(TokenStream::from(quote!(type ComponentAttrs =))); component_impl_stream.extend(TokenStream::from(quote!(
component_impl_stream.extend(Some(TokenTree::Ident(attributes_ident.clone()))); type ComponentAttrs = #attributes_ident;
component_impl_stream.extend(TokenStream::from(quote!(;))); type PartialComponentAttrs = #partial_attributes_ident;
component_impl_stream.extend(TokenStream::from(quote!(type PartialComponentAttrs =))); type ReactiveComponentAttrs = #reactive_attributes_ident;
component_impl_stream.extend(Some(TokenTree::Ident(partial_attributes_ident.clone()))); )));
component_impl_stream.extend(TokenStream::from(quote!(;)));
component_impl_stream.extend(TokenStream::from(quote!(const UPDATE_LENGTH : usize =)));
component_impl_stream.extend(Some(TokenTree::Literal(Literal::usize_unsuffixed(f64::ceil((attributes.len() + reactive_variables.len()) as f64 / 32 as f64) as usize))));
component_impl_stream.extend(TokenStream::from(quote!(;)));
// fn new // fn new
component_impl_stream.extend(TokenStream::from(quote!(fn new(attrs: Self::ComponentAttrs, selfref: rusalka::WeakSharedComponent<Self>) -> Self))); component_impl_stream.extend(TokenStream::from(quote!(
fn new(attrs: Self::ComponentAttrs, selfref: rusalka::WeakSharedComponent<Self>) -> Self
)));
let mut new_stream = TokenStream::new(); let mut new_stream = TokenStream::new();
for variable in &reactive_variables { new_stream.extend(TokenStream::from(quote!(
new_stream.extend(TokenStream::from(quote!(let))); let attrs: Self::ReactiveComponentAttrs = attrs.into();
new_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); )));
new_stream.extend(TokenStream::from(quote!(:)));
new_stream.extend(TokenStream::from(quote!(std::sync::Arc<std::sync::Mutex<rusalka::invalidator::Invalidator<)));
new_stream.extend(variable.type_.clone());
new_stream.extend(TokenStream::from(quote!(>>>)));
new_stream.extend(TokenStream::from(quote!(=)));
let mut invalidator = TokenStream::from(quote!(rusalka::invalidator::Invalidator::new)); for attribute in &attributes {
let name = attribute.name.clone();
new_stream.extend(TokenStream::from(quote!(
let #name = attrs.#name.clone();
)));
}
for variable in &reactive_variables {
let name = variable.name.clone();
let type_ = variable.type_.clone();
let mut invalidator_inner = TokenStream::new(); let mut invalidator_inner = TokenStream::new();
if let Some(def) = &variable.default { if let Some(def) = &variable.default {
invalidator_inner.extend(replace_variables(def.clone()).1); invalidator_inner.extend(replace_variables(def.clone()).1);
} else { } else {
invalidator_inner.extend(TokenStream::from(quote!(Default::default()))); invalidator_inner.extend(TokenStream::from(quote!(Default::default())));
} }
invalidator.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, invalidator_inner)))); new_stream.extend(TokenStream::from(quote!(
new_stream.extend(wrap_in_arc_mutex(invalidator)); 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 )));
new_stream.extend(TokenStream::from(quote!(;))); )));
} }
new_stream.extend(main_logic); new_stream.extend(main_logic);
@ -466,20 +530,20 @@ pub fn make_component(item: TokenStream) -> TokenStream {
let mut component_new_stream = TokenStream::new(); let mut component_new_stream = TokenStream::new();
// The following would allow not importing ComponentAttributes, but rust doesn't support it outside of nightly just yet // The following would allow not importing ComponentAttributes, but rust doesn't support it outside of nightly just yet
// component_new_stream.extend(Some(TokenTree::Punct(Punct::new('<', proc_macro::Spacing::Alone)))); // component_new_stream.extend(Some(TokenTree::Punct(Punct::new('<', Spacing::Alone))));
// component_new_stream.extend(Some(TokenTree::Ident(component_name.clone()))); // component_new_stream.extend(Some(TokenTree::Ident(component_name.clone())));
// component_new_stream.extend(TokenStream::from(quote!(as Component>::ComponentAttrs))); // component_new_stream.extend(TokenStream::from(quote!(as Component>::ComponentAttrs)));
component_new_stream.extend(Some(TokenTree::Ident(Ident::new(&format!("{}Attributes", component_name), component_name.span())))); component_new_stream.extend(Some(TokenTree::Ident(Ident::new(&format!("{}Attributes", component_name), component_name.span()))));
let (_reactive_variables, subcomponent_stream) = replace_variables(component.contents.clone()); let (_reactive_variables, subcomponent_stream) = replace_variables(component.contents.clone());
let components_attributes_group = Group::new(proc_macro::Delimiter::Brace, subcomponent_stream); let components_attributes_group = Group::new(Delimiter::Brace, subcomponent_stream);
component_new_stream.extend(Some(TokenTree::Group(components_attributes_group))); component_new_stream.extend(Some(TokenTree::Group(components_attributes_group)));
component_new_stream.extend(TokenStream::from(quote!(, cselfref.clone()))); component_new_stream.extend(TokenStream::from(quote!(, cselfref.clone())));
let component_new_group = Group::new(proc_macro::Delimiter::Parenthesis, component_new_stream); let component_new_group = Group::new(Delimiter::Parenthesis, component_new_stream);
component_stream.extend(Some(TokenTree::Group(component_new_group))); component_stream.extend(Some(TokenTree::Group(component_new_group)));
@ -487,27 +551,44 @@ pub fn make_component(item: TokenStream) -> TokenStream {
}, },
ComponentType::Node => { ComponentType::Node => {
let (_reactive_variables, subcomponent_stream) = replace_variables(component.contents.clone()); let (_reactive_variables, subcomponent_stream) = replace_variables(component.contents.clone());
let node_group = Group::new(proc_macro::Delimiter::Brace, subcomponent_stream); let node_group = Group::new(Delimiter::Brace, subcomponent_stream);
component_stream.extend(Some(TokenTree::Group(node_group))); component_stream.extend(Some(TokenTree::Group(node_group)));
new_returnvalue_stream.extend(wrap_in_arcrwlock(component_stream)); new_returnvalue_stream.extend(wrap_in_arcrwlock(component_stream));
} }
} }
new_returnvalue_stream.extend(TokenStream::from(quote!(,))); new_returnvalue_stream.extend(quote!(,));
i+=1; i+=1;
} }
new_returnvalue_stream.extend(TokenStream::from(quote!(attrs, selfref,))); new_returnvalue_stream.extend(quote!(attrs, selfref,));
let mut i = 0u32;
for reactive_block in &reactive_blocks {
let ident = format_ident!("sub{}", i);
let content = &reactive_block.contents;
let variables = &reactive_block.variables;
new_returnvalue_stream.extend(quote!(
#ident: {
#(let #variables = #variables.clone();)*
[#(#variables.clone().lock().unwrap()),*].subscribe(Box::new(move || {
#(let #variables = #variables.clone();)*
#content
}))
},
));
i+=1;
}
for variable in &reactive_variables { for variable in &reactive_variables {
new_returnvalue_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); new_returnvalue_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
new_returnvalue_stream.extend(TokenStream::from(quote!(,))); new_returnvalue_stream.extend(quote!(,));
} }
let new_returnvalue_group = Group::new(proc_macro::Delimiter::Brace, new_returnvalue_stream); let new_returnvalue_group = Group::new(Delimiter::Brace, new_returnvalue_stream);
new_stream.extend(Some(TokenTree::Group(new_returnvalue_group))); new_stream.extend(Some(TokenTree::Group(new_returnvalue_group)));
new_stream.extend(TokenStream::from(quote!(;))); new_stream.extend(quote!(;));
i = 0; i = 0;
for component in &components_used { for component in &components_used {
@ -551,17 +632,15 @@ pub fn make_component(item: TokenStream) -> TokenStream {
inner_callback_stream.extend(replace_variables(event_listener.callback.clone().stream()).1); inner_callback_stream.extend(replace_variables(event_listener.callback.clone().stream()).1);
inner_callback_stream.extend(TokenStream::from(quote!(this.tick(None);))); let callback_group = Group::new(Delimiter::Brace, inner_callback_stream);
let callback_group = Group::new(proc_macro::Delimiter::Brace, inner_callback_stream);
callback_stream.extend(Some(TokenTree::Group(callback_group))); callback_stream.extend(Some(TokenTree::Group(callback_group)));
let callback_group = Group::new(proc_macro::Delimiter::Parenthesis, callback_stream); let callback_group = Group::new(Delimiter::Parenthesis, callback_stream);
box_stream.extend(Some(TokenTree::Group(callback_group))); box_stream.extend(Some(TokenTree::Group(callback_group)));
let box_group = Group::new(proc_macro::Delimiter::Parenthesis, box_stream); let box_group = Group::new(Delimiter::Parenthesis, box_stream);
new_stream.extend(Some(TokenTree::Group(box_group))); new_stream.extend(Some(TokenTree::Group(box_group)));
@ -572,7 +651,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
new_stream.extend(TokenStream::from(quote!(this))); new_stream.extend(TokenStream::from(quote!(this)));
let new_group = Group::new(proc_macro::Delimiter::Brace, new_stream); let new_group = Group::new(Delimiter::Brace, new_stream);
component_impl_stream.extend(Some(TokenTree::Group(new_group))); component_impl_stream.extend(Some(TokenTree::Group(new_group)));
// fn set // fn set
@ -581,14 +660,13 @@ pub fn make_component(item: TokenStream) -> TokenStream {
let mut set_stream = TokenStream::new(); let mut set_stream = TokenStream::new();
if attributes.len() > 0 { if attributes.len() > 0 {
set_stream.extend(TokenStream::from(quote!(let mut to_update = [0; Self::UPDATE_LENGTH];)));
for (i, attribute) in attributes.iter().enumerate() { for (i, attribute) in attributes.iter().enumerate() {
set_stream.extend(TokenStream::from(quote!(if let Some))); set_stream.extend(TokenStream::from(quote!(if let Some)));
let mut some_inner = TokenStream::new(); let mut some_inner = TokenStream::new();
some_inner.extend(Some(TokenTree::Ident(attribute.name.clone()))); some_inner.extend(Some(TokenTree::Ident(attribute.name.clone())));
set_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, some_inner)))); set_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Parenthesis, some_inner))));
set_stream.extend(TokenStream::from(quote!(= attrs.))); set_stream.extend(TokenStream::from(quote!(= attrs.)));
set_stream.extend(Some(TokenTree::Ident(attribute.name.clone()))); set_stream.extend(Some(TokenTree::Ident(attribute.name.clone())));
@ -597,31 +675,25 @@ pub fn make_component(item: TokenStream) -> 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!(=))); set_stream_inner.extend(TokenStream::from(quote!(.lock().unwrap().set)));
set_stream_inner.extend(Some(TokenTree::Ident(attribute.name.clone())));
set_stream_inner.extend(TokenStream::from(quote!(; to_update)));
let mut to_update_stream = TokenStream::new(); let mut set_stream_inner_inner = TokenStream::new();
to_update_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(i as u32 / 32)))); set_stream_inner_inner.extend(Some(TokenTree::Ident(attribute.name.clone())));
set_stream_inner.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Bracket, to_update_stream)))); set_stream_inner.extend(Some(TokenTree::Group(Group::new(Delimiter::Parenthesis, set_stream_inner_inner))));
set_stream_inner.extend(TokenStream::from(quote!(;)));
set_stream_inner.extend(TokenStream::from(quote!(|= ))); set_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, set_stream_inner))));
set_stream_inner.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(1 << (i as u32 % 32)))));
set_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, set_stream_inner))));
} }
set_stream.extend(TokenStream::from(quote!(if to_update.into_iter().reduce(|a,b| a+b).unwrap() != 0 { self.tick(Some(&to_update)); })));
} }
component_impl_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, set_stream)))); component_impl_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, set_stream))));
// fn get // fn get
component_impl_stream.extend(TokenStream::from(quote!(fn get(&self) -> &Self::ComponentAttrs { &self.attrs }))); component_impl_stream.extend(TokenStream::from(quote!(fn get(&self) -> &Self::ReactiveComponentAttrs { &self.attrs })));
// fn mount // fn mount
@ -652,7 +724,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
node_insert_self_stream.extend(Some(TokenTree::Ident(parent_ident))); node_insert_self_stream.extend(Some(TokenTree::Ident(parent_ident)));
node_insert_self_stream.extend(TokenStream::from(quote!(.clone()))); node_insert_self_stream.extend(TokenStream::from(quote!(.clone())));
let node_insert_self_group = Group::new(proc_macro::Delimiter::Brace, node_insert_self_stream); let node_insert_self_group = Group::new(Delimiter::Brace, node_insert_self_stream);
component_mount_stream.extend(TokenStream::from(quote!(&))); component_mount_stream.extend(TokenStream::from(quote!(&)));
component_mount_stream.extend(Some(TokenTree::Group(node_insert_self_group))); component_mount_stream.extend(Some(TokenTree::Group(node_insert_self_group)));
}, },
@ -663,7 +735,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
component_mount_stream.extend(TokenStream::from(quote!(,))); component_mount_stream.extend(TokenStream::from(quote!(,)));
component_mount_stream.extend(TokenStream::from(quote!(before))); component_mount_stream.extend(TokenStream::from(quote!(before)));
let component_mount_group = Group::new(proc_macro::Delimiter::Parenthesis, component_mount_stream); let component_mount_group = Group::new(Delimiter::Parenthesis, component_mount_stream);
mount_stream.extend(Some(TokenTree::Group(component_mount_group))); mount_stream.extend(Some(TokenTree::Group(component_mount_group)));
@ -686,7 +758,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
node_insert_self_stream.extend(Some(TokenTree::Ident(parent_ident))); node_insert_self_stream.extend(Some(TokenTree::Ident(parent_ident)));
node_insert_self_stream.extend(TokenStream::from(quote!(.clone()))); node_insert_self_stream.extend(TokenStream::from(quote!(.clone())));
let node_insert_self_group = Group::new(proc_macro::Delimiter::Brace, node_insert_self_stream); let node_insert_self_group = Group::new(Delimiter::Brace, node_insert_self_stream);
node_insert_stream.extend(TokenStream::from(quote!(&))); node_insert_stream.extend(TokenStream::from(quote!(&)));
node_insert_stream.extend(Some(TokenTree::Group(node_insert_self_group))); node_insert_stream.extend(Some(TokenTree::Group(node_insert_self_group)));
}, },
@ -704,13 +776,13 @@ pub fn make_component(item: TokenStream) -> TokenStream {
node_insert_self_stream.extend(Some(TokenTree::Ident(ident))); node_insert_self_stream.extend(Some(TokenTree::Ident(ident)));
node_insert_self_stream.extend(TokenStream::from(quote!(.clone()))); node_insert_self_stream.extend(TokenStream::from(quote!(.clone())));
let node_insert_self_group = Group::new(proc_macro::Delimiter::Brace, node_insert_self_stream); let node_insert_self_group = Group::new(Delimiter::Brace, node_insert_self_stream);
node_insert_stream.extend(Some(TokenTree::Group(node_insert_self_group))); node_insert_stream.extend(Some(TokenTree::Group(node_insert_self_group)));
node_insert_stream.extend(TokenStream::from(quote!(,))); node_insert_stream.extend(TokenStream::from(quote!(,)));
node_insert_stream.extend(TokenStream::from(quote!(before))); node_insert_stream.extend(TokenStream::from(quote!(before)));
node_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, node_insert_stream)))); node_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Parenthesis, node_insert_stream))));
mount_stream.extend(node_stream); mount_stream.extend(node_stream);
mount_stream.extend(TokenStream::from(quote!(;))); mount_stream.extend(TokenStream::from(quote!(;)));
@ -719,7 +791,7 @@ pub fn make_component(item: TokenStream) -> TokenStream {
} }
let mount_group = Group::new(proc_macro::Delimiter::Brace, mount_stream); let mount_group = Group::new(Delimiter::Brace, mount_stream);
component_impl_stream.extend(Some(TokenTree::Group(mount_group))); component_impl_stream.extend(Some(TokenTree::Group(mount_group)));
// fn unmount // fn unmount
@ -755,219 +827,29 @@ pub fn make_component(item: TokenStream) -> TokenStream {
node_detach_self_stream.extend(Some(TokenTree::Ident(ident))); node_detach_self_stream.extend(Some(TokenTree::Ident(ident)));
node_detach_self_stream.extend(TokenStream::from(quote!(.clone()))); node_detach_self_stream.extend(TokenStream::from(quote!(.clone())));
let node_detach_self_group = Group::new(proc_macro::Delimiter::Brace, node_detach_self_stream); let node_detach_self_group = Group::new(Delimiter::Brace, node_detach_self_stream);
node_detach_stream.extend(Some(TokenTree::Group(node_detach_self_group))); node_detach_stream.extend(Some(TokenTree::Group(node_detach_self_group)));
let node_detach_group = Group::new(proc_macro::Delimiter::Parenthesis, node_detach_stream); let node_detach_group = Group::new(Delimiter::Parenthesis, node_detach_stream);
unmount_stream.extend(Some(TokenTree::Group(node_detach_group))); unmount_stream.extend(Some(TokenTree::Group(node_detach_group)));
unmount_stream.extend(TokenStream::from(quote!(;))); unmount_stream.extend(TokenStream::from(quote!(;)));
} }
} }
} }
let unmount_group = Group::new(proc_macro::Delimiter::Brace, unmount_stream); let unmount_group = Group::new(Delimiter::Brace, unmount_stream);
component_impl_stream.extend(Some(TokenTree::Group(unmount_group))); component_impl_stream.extend(Some(TokenTree::Group(unmount_group)));
// fn update output.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, component_impl_stream))));
let mut all_variables = Vec::new();
all_variables.extend(attributes.clone());
all_variables.extend(reactive_variables.clone());
component_impl_stream.extend(TokenStream::from(quote!(fn update(&self, bitmap: &[u32]))));
let mut update_stream = TokenStream::new();
update_stream.extend(TokenStream::from(quote!(
self.check_update(bitmap);
let attrs = &self.attrs;
)));
for variable in &reactive_variables {
update_stream.extend(TokenStream::from(quote!(let)));
update_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
update_stream.extend(TokenStream::from(quote!(=)));
update_stream.extend(TokenStream::from(quote!(&self.)));
update_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
update_stream.extend(TokenStream::from(quote!(;)));
}
'block: for block in &reactive_blocks {
let mut keys: Vec<u32> = vec![0; all_variables.len() / 32 + 1];
for variable in &block.variables {
let index = all_variables.iter().position(|x| x.name.to_string() == variable.to_string());
if let None = index {
eprintln!("Warning: variable {} not found in component {}", variable, str_name);
continue 'block;
}
let index = index.unwrap();
let array_offset = index / 32;
let num_offset = index % 32;
*keys.get_mut(array_offset).unwrap() |= 1 << num_offset;
}
if keys.iter().all(|x| *x == 0) {
continue 'block;
}
update_stream.extend(TokenStream::from(quote!(if)));
let mut i = 0;
for key in keys {
if i > 0 {
update_stream.extend(TokenStream::from(quote!(||)));
}
update_stream.extend(TokenStream::from(quote!(bitmap)));
let mut ifgroup_stream = TokenStream::new();
// update_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(key))));
ifgroup_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(i))));
update_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Bracket, ifgroup_stream))));
update_stream.extend(TokenStream::from(quote!(&)));
update_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(key))));
update_stream.extend(TokenStream::from(quote!(!= 0)));
i += 1;
}
let inner_group = Group::new(proc_macro::Delimiter::Brace, block.contents.clone());
update_stream.extend(Some(TokenTree::Group(inner_group)));
}
let mut component_index = 0;
'block: for component in &components_used {
for (prop, block) in &component.reactive_props {
let mut keys: Vec<u32> = vec![0; all_variables.len() / 32 + 1];
for variable in &block.variables {
let index = all_variables.iter().position(|x| x.name.to_string() == variable.to_string());
if let None = index {
eprintln!("Warning: variable {} not found in component {}", variable, str_name);
continue 'block;
}
let index = index.unwrap();
let array_offset = index / 32;
let num_offset = index % 32;
*keys.get_mut(array_offset).unwrap() |= 1 << num_offset;
}
update_stream.extend(TokenStream::from(quote!(if)));
let mut i = 0;
for key in keys {
if i > 0 {
update_stream.extend(TokenStream::from(quote!(||)));
}
update_stream.extend(TokenStream::from(quote!(bitmap)));
let mut ifgroup_stream = TokenStream::new();
// update_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(key))));
ifgroup_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(i))));
update_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Bracket, ifgroup_stream))));
update_stream.extend(TokenStream::from(quote!(&)));
update_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(key))));
update_stream.extend(TokenStream::from(quote!(!= 0)));
i += 1;
}
let mut inner_stream = TokenStream::new();
match component.component_type {
ComponentType::Node => {
inner_stream.extend(TokenStream::from(quote!(self.)));
inner_stream.extend(Some(TokenTree::Ident(Ident::new(&format!("comp{}", component_index), Span::call_site()))));
inner_stream.extend(TokenStream::from(quote!(.write().unwrap().)));
inner_stream.extend(Some(TokenTree::Ident(block.prop_ident.clone().unwrap())));
inner_stream.extend(TokenStream::from(quote!( = )));
inner_stream.extend(replace_variables(block.contents.clone()).1);
},
ComponentType::RealComponent => {
inner_stream.extend(TokenStream::from(quote!(self.)));
inner_stream.extend(Some(TokenTree::Ident(Ident::new(&format!("comp{}", component_index), Span::call_site()))));
inner_stream.extend(TokenStream::from(quote!(.lock().unwrap().set)));
let mut component_set_stream = TokenStream::new();
component_set_stream.extend(Some(TokenTree::Ident(block.prop_ident.clone().unwrap())));
component_set_stream.extend(TokenStream::from(quote!(: Option::Some)));
let component_set_some_stream = replace_variables(block.contents.clone()).1;
let component_set_group = Group::new(proc_macro::Delimiter::Parenthesis, component_set_some_stream);
component_set_stream.extend(Some(TokenTree::Group(component_set_group)));
component_set_stream.extend(TokenStream::from(quote!(, ..Default::default())));
let component_set_group = Group::new(proc_macro::Delimiter::Brace, component_set_stream);
let mut component_set_outer_stream = TokenStream::new();
let name = component.name.clone().to_string();
component_set_outer_stream.extend(Some(TokenTree::Ident(Ident::new(&format!("Partial{}Attributes", name), component.name.span()))));
component_set_outer_stream.extend(Some(TokenTree::Group(component_set_group)));
let component_set_outer_group = Group::new(proc_macro::Delimiter::Parenthesis, component_set_outer_stream);
inner_stream.extend(Some(TokenTree::Group(component_set_outer_group)));
}
}
let inner_group = Group::new(proc_macro::Delimiter::Brace, inner_stream);
update_stream.extend(Some(TokenTree::Group(inner_group)));
}
component_index += 1;
}
let update_group = Group::new(proc_macro::Delimiter::Brace, update_stream);
component_impl_stream.extend(Some(TokenTree::Group(update_group)));
// fn tick
component_impl_stream.extend(TokenStream::from(quote!(fn tick(&mut self, inbitmap: Option<&[u32]>))));
let mut tick_stream = TokenStream::new();
tick_stream.extend(TokenStream::from(quote!(
let mut bitmap = [0; Self::UPDATE_LENGTH];
if let Some(inbitmap) = inbitmap {
bitmap.clone_from_slice(inbitmap);
}
self.check_update(&bitmap);
)));
let mut i = attributes.len() as u32;
for variable in &reactive_variables {
tick_stream.extend(TokenStream::from(quote!(if self.)));
tick_stream.extend(Some(TokenTree::Ident(variable.name.clone())));
tick_stream.extend(TokenStream::from(quote!(.lock().unwrap().invalidated())));
let mut if_stream = TokenStream::new();
let array_offset = i / 32;
let num_offset = i % 32;
if_stream.extend(TokenStream::from(quote!(bitmap)));
let mut ifgroup_stream = TokenStream::new();
ifgroup_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(array_offset))));
if_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Bracket, ifgroup_stream))));
if_stream.extend(TokenStream::from(quote!(|= )));
if_stream.extend(Some(TokenTree::Literal(Literal::u32_unsuffixed(1 << num_offset))));
tick_stream.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, if_stream))));
i += 1;
}
tick_stream.extend(TokenStream::from(quote!(
if bitmap.into_iter().reduce(|a, b| a + b).unwrap() != 0 {
self.update(&bitmap);
}
)));
let tick_group = Group::new(proc_macro::Delimiter::Brace, tick_stream);
component_impl_stream.extend(Some(TokenTree::Group(tick_group)));
output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, component_impl_stream))));
println!("{}", output.to_string()); println!("{}", output.to_string());
println!(); println!();
output output.into()
} }
/// Replaces $variable with **variable.lock().unwrap() /// Replaces $variable with **variable.lock().unwrap().guard()
/// Returns the found variables as well as tokenstream with replaced variables /// Returns the found variables as well as tokenstream with replaced variables
/// Returned vec is sorted and deduplicated /// Returned vec is sorted and deduplicated
fn replace_variables(stream: TokenStream) -> (Vec<Ident>, TokenStream) { fn replace_variables(stream: TokenStream) -> (Vec<Ident>, TokenStream) {
@ -987,7 +869,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()))); output.extend(TokenStream::from(quote!(.lock().unwrap().guard())));
}, },
TokenTree::Group(group) => { TokenTree::Group(group) => {
let group_delim = group.delimiter(); let group_delim = group.delimiter();
@ -1015,7 +897,7 @@ fn wrap_in_arc_mutex(stream: TokenStream) -> TokenStream {
output.extend(TokenStream::from(quote!(std::sync::Arc::new))); output.extend(TokenStream::from(quote!(std::sync::Arc::new)));
let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); let mutex_group = Group::new(Delimiter::Parenthesis, stream);
let mut mutex_stream = TokenStream::new(); let mut mutex_stream = TokenStream::new();
@ -1023,7 +905,7 @@ fn wrap_in_arc_mutex(stream: TokenStream) -> TokenStream {
mutex_stream.extend(Some(TokenTree::Group(mutex_group))); mutex_stream.extend(Some(TokenTree::Group(mutex_group)));
let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, mutex_stream); let arc_group = Group::new(Delimiter::Parenthesis, mutex_stream);
output.extend(Some(TokenTree::Group(arc_group))); output.extend(Some(TokenTree::Group(arc_group)));
@ -1035,7 +917,7 @@ fn wrap_in_arcmutex_cyclic(stream: TokenStream) -> TokenStream {
output.extend(TokenStream::from(quote!(std::sync::Arc::new_cyclic))); output.extend(TokenStream::from(quote!(std::sync::Arc::new_cyclic)));
let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); let mutex_group = Group::new(Delimiter::Parenthesis, stream);
let mut mutex_stream = TokenStream::new(); let mut mutex_stream = TokenStream::new();
@ -1045,7 +927,7 @@ fn wrap_in_arcmutex_cyclic(stream: TokenStream) -> TokenStream {
mutex_stream.extend(Some(TokenTree::Group(mutex_group))); mutex_stream.extend(Some(TokenTree::Group(mutex_group)));
let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, mutex_stream); let arc_group = Group::new(Delimiter::Parenthesis, mutex_stream);
output.extend(Some(TokenTree::Group(arc_group))); output.extend(Some(TokenTree::Group(arc_group)));
@ -1057,7 +939,7 @@ fn wrap_in_arcrwlock(stream: TokenStream) -> TokenStream {
output.extend(TokenStream::from(quote!(std::sync::Arc::new))); output.extend(TokenStream::from(quote!(std::sync::Arc::new)));
let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); let mutex_group = Group::new(Delimiter::Parenthesis, stream);
let mut rwlock_stream = TokenStream::new(); let mut rwlock_stream = TokenStream::new();
@ -1065,7 +947,7 @@ fn wrap_in_arcrwlock(stream: TokenStream) -> TokenStream {
rwlock_stream.extend(Some(TokenTree::Group(mutex_group))); rwlock_stream.extend(Some(TokenTree::Group(mutex_group)));
let arc_group = Group::new(proc_macro::Delimiter::Parenthesis, rwlock_stream); let arc_group = Group::new(Delimiter::Parenthesis, rwlock_stream);
output.extend(Some(TokenTree::Group(arc_group))); output.extend(Some(TokenTree::Group(arc_group)));

View file

@ -4,25 +4,18 @@ use crate::WeakSharedComponent;
/// A rusalka component /// A rusalka component
pub trait Component { pub trait Component {
type ComponentAttrs: Default; type ComponentAttrs;
type ReactiveComponentAttrs: From<Self::ComponentAttrs>;
type PartialComponentAttrs: Default + From<Self::ComponentAttrs>; type PartialComponentAttrs: Default + From<Self::ComponentAttrs>;
const UPDATE_LENGTH : usize = 0; const UPDATE_LENGTH : usize = 0;
fn new(attr: Self::ComponentAttrs, selfref: WeakSharedComponent<Self>) -> Self; fn new(attr: Self::ComponentAttrs, selfref: WeakSharedComponent<Self>) -> Self;
fn get(&self) -> &Self::ComponentAttrs; fn get(&self) -> &Self::ReactiveComponentAttrs;
fn set(&mut self, attr: Self::PartialComponentAttrs); fn set(&mut self, attr: Self::PartialComponentAttrs);
fn mount(&self, parent: &SharedNode, before: Option<&SharedNode>); fn mount(&self, parent: &SharedNode, before: Option<&SharedNode>);
fn update(&self, bitmap: &[u32]);
fn unmount(&self); fn unmount(&self);
fn tick(&mut self, bitmap: Option<&[u32]>) {
if let Some(bitmap) = bitmap {
self.update(bitmap);
}
}
// fn set_selfref(&mut self, selfref: SharedComponent<Self>);
fn check_update(&self, bitmap: &[u32]) -> () {
if bitmap.len() != Self::UPDATE_LENGTH {
panic!("Bitmap length does not match update length");
}
}
} }
pub struct Slot {
pub mount: Box<dyn FnMut(&SharedNode, Option<&SharedNode>)>,
pub unmount: Box<dyn FnMut()>,
}

View file

@ -1,36 +0,0 @@
use std::ops::{Deref, DerefMut};
pub struct Invalidator<T> {
inner: T,
invalidated: bool
}
impl<T> Invalidator<T> {
pub fn new(inner: T) -> Self {
Self {
inner,
invalidated: false
}
}
pub fn reset(&mut self) {
self.invalidated = false;
}
pub fn invalidated(&self) -> bool {
self.invalidated
}
}
impl<T> Deref for Invalidator<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for Invalidator<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.invalidated = true;
&mut self.inner
}
}

View file

@ -5,7 +5,6 @@ use mangui::nodes::Node;
pub mod component; pub mod component;
pub mod nodes; pub mod nodes;
pub mod invalidator;
pub mod store; pub mod store;
pub type SharedComponent<T: Component> = Arc<Mutex<T>>; pub type SharedComponent<T: Component> = Arc<Mutex<T>>;

View file

@ -1,8 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use mangui::SharedNode; use mangui::SharedNode;
pub mod primitives;
pub fn detach(node: &SharedNode) { pub fn detach(node: &SharedNode) {
if let Some(parent) = node.read().unwrap().parent() { if let Some(parent) = node.read().unwrap().parent() {
parent.write().unwrap().remove_child(node).unwrap(); parent.write().unwrap().remove_child(node).unwrap();

View file

@ -1,91 +0,0 @@
// DemoComponent
use std::sync::{Arc, RwLock};
use mangui::{SharedNode, nodes::{primitives, Style}, taffy::prelude::Size, femtovg::{Paint, Color}};
use crate::{component::Component, WeakSharedComponent};
use super::{insert, detach};
pub struct Rectangle {
node: Arc<RwLock<primitives::Rectangle>>,
attrs: RectangleAttributes,
selfref: WeakSharedComponent<Self>
}
#[derive(Default)]
pub struct RectangleAttributes {
pub radius: f32
}
#[derive(Default)]
pub struct PartialRectangleAttributes {
pub radius: Option<f32>
}
impl From<RectangleAttributes> for PartialRectangleAttributes {
fn from(attrs: RectangleAttributes) ->
Self {
Self {
radius: Some(attrs.radius)
}
}
}
impl Component for Rectangle {
type ComponentAttrs = RectangleAttributes;
type PartialComponentAttrs = PartialRectangleAttributes;
const UPDATE_LENGTH : usize = 1;
fn new(attrs: Self::ComponentAttrs, selfref: WeakSharedComponent<Self>) -> Self {
Self {
node: Arc::new(RwLock::new(primitives::Rectangle {
style: Style {
layout: mangui::nodes::TaffyStyle {
min_size: Size {
width: mangui::taffy::style::Dimension::Length(50.),
height: mangui::taffy::style::Dimension::Length(100.)
},
..Default::default()
},
..Default::default()
},
fill: Paint::color(Color::rgb(0, 0, 255)),
radius: attrs.radius,
..Default::default()
})),
attrs,
selfref
}
}
fn set(&mut self, attrs: Self::PartialComponentAttrs) {
let mut to_update = [0; Self::UPDATE_LENGTH];
if let Some(radius) = attrs.radius {
self.attrs.radius = radius;
to_update[0] |= 1;
}
if to_update.into_iter().reduce(|a,b| a+b).unwrap() != 0 {
self.update(&to_update);
}
}
fn get(&self) -> &Self::ComponentAttrs { &self.attrs }
fn mount(&self, parent: &SharedNode, before: Option<&SharedNode>) {
insert(parent, &{self.node.clone()}, before);
}
fn update(&self, bitmap: &[u32]) {
self.check_update(bitmap);
if bitmap[0] & 1 != 0 {
self.node.write().unwrap().radius = self.attrs.radius;
}
if bitmap[0] & 1 != 0 {}
}
fn unmount(&self) {
detach(&{self.node.clone()});
}
}

View file

@ -1,30 +1,33 @@
use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak}; use std::ops::{Deref, DerefMut};
use std::sync::{atomic::AtomicU64, Arc, Mutex, Weak, MutexGuard};
/// Unsubscribes from the store when dropped /// Unsubscribes from the store when dropped
pub trait StoreUnsubscribe: Drop {} pub trait StoreUnsubscribe: Drop {}
pub trait ReadableStore { pub trait Signal {
fn subscribe(&self, callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe>;
}
pub trait ReadableStore: Signal {
type State; type State;
fn get(&self) -> &Self::State; fn get(&self) -> &Self::State;
fn subscribe(&self, callback: Box<dyn FnMut(&Self::State) -> ()>) -> Box<dyn StoreUnsubscribe>;
} }
pub trait WritableStore: ReadableStore { pub trait WritableStore: ReadableStore {
fn set(&mut self, state: Self::State); fn set(&mut self, state: Self::State);
} }
struct Listener<T> { struct Listener {
hash: u64, hash: u64,
callback: Box<dyn FnMut(&T) -> ()> callback: Box<dyn FnMut()>
} }
struct ReadableUnsubscribe {
struct ReadableUnsubscribe<T> { listeners: Weak<Mutex<Vec<Listener>>>,
listeners: Weak<Mutex<Vec<Listener<T>>>>,
hash: u64 hash: u64
} }
impl<T: Sized> Drop for ReadableUnsubscribe<T> { impl Drop for ReadableUnsubscribe {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(listeners) = self.listeners.upgrade() { if let Some(listeners) = self.listeners.upgrade() {
let mut listeners = listeners.lock().unwrap(); let mut listeners = listeners.lock().unwrap();
@ -33,11 +36,11 @@ impl<T: Sized> Drop for ReadableUnsubscribe<T> {
} }
} }
impl<T> StoreUnsubscribe for ReadableUnsubscribe<T> {} impl StoreUnsubscribe for ReadableUnsubscribe {}
pub struct Readable<T> { pub struct Readable<T> {
state: T, state: T,
listeners: Arc<Mutex<Vec<Listener<T>>>> listeners: Arc<Mutex<Vec<Listener>>>
} }
impl<T> Readable<T> { impl<T> Readable<T> {
@ -51,14 +54,11 @@ impl<T> Readable<T> {
static CALL_COUNT: AtomicU64 = AtomicU64::new(0); static CALL_COUNT: AtomicU64 = AtomicU64::new(0);
impl<T: 'static> ReadableStore for Readable<T> { impl<T: 'static> Signal for Readable<T> {
type State = T; fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
fn get(&self) -> &Self::State {
&self.state
}
fn subscribe(&self, callback: Box<dyn FnMut(&Self::State) -> ()>) -> 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
@ -70,9 +70,16 @@ impl<T: 'static> ReadableStore for Readable<T> {
} }
} }
impl<T: 'static> ReadableStore for Readable<T> {
type State = T;
fn get(&self) -> &Self::State {
&self.state
}
}
pub struct Writable<T> { pub struct Writable<T> {
state: T, state: T,
listeners: Arc<Mutex<Vec<Listener<T>>>> listeners: Arc<Mutex<Vec<Listener>>>
} }
impl<T> Writable<T> { impl<T> Writable<T> {
@ -84,14 +91,11 @@ impl<T> Writable<T> {
} }
} }
impl<T: 'static> ReadableStore for Writable<T> { impl<T: 'static> Signal for Writable<T> {
type State = T; fn subscribe(&self, mut callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
fn get(&self) -> &Self::State {
&self.state
}
fn subscribe(&self, callback: Box<dyn FnMut(&Self::State) -> ()>) -> 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
@ -103,12 +107,112 @@ impl<T: 'static> ReadableStore for Writable<T> {
} }
} }
impl<T: 'static> ReadableStore for Writable<T> {
type State = T;
fn get(&self) -> &Self::State {
&self.state
}
}
impl<T: 'static> WritableStore for Writable<T> { impl<T: 'static> WritableStore for Writable<T> {
fn set(&mut self, state: Self::State) { fn set(&mut self, state: Self::State) {
self.state = state; self.state = 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)(&self.state); (listener.callback)();
} }
} }
}
impl<T: 'static> Default for Writable<T> where T: Default {
fn default() -> Self {
Self::new(Default::default())
}
}
pub struct DerefGuard<T> {
listeners: Arc<Mutex<Vec<Listener>>>,
tainted: bool,
inner: T
}
pub trait DerefGuardExt<T>: WritableStore + Signal {
fn guard(&mut self) -> DerefGuard<&mut T>;
}
impl <T: 'static> DerefGuardExt<T> for Writable<T> {
fn guard(&mut self) -> DerefGuard<&mut T> {
DerefGuard {
listeners: self.listeners.clone(),
tainted: false,
inner: &mut self.state
}
}
}
impl<T> Deref for DerefGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for DerefGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.tainted = true;
&mut self.inner
}
}
impl<T> Drop for DerefGuard<T> {
fn drop(&mut self) {
if self.tainted {
let mut listeners = self.listeners.lock().unwrap();
for listener in listeners.iter_mut() {
(listener.callback)();
}
}
}
}
struct VecUnsub {
unsubscribes: Vec<Box<dyn StoreUnsubscribe>>
}
impl Drop for VecUnsub {
fn drop(&mut self) {}
}
impl StoreUnsubscribe for VecUnsub {}
// odd that I have to implement this but whatever makes the compiler happy
impl<T: Signal> Signal for MutexGuard<'_, 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
fn subscribe(&self, callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
let mut unsubscribes = Vec::with_capacity(self.len());
let callback = Arc::new(Mutex::new(callback));
for signal in self.iter() {
let callback = callback.clone();
unsubscribes.push(signal.subscribe(Box::new(move || callback.lock().unwrap()())));
}
Box::new(VecUnsub { unsubscribes })
}
}
impl<T: Signal> Signal for [T] {
/// Subscribes to all signals in the array
fn subscribe(&self, callback: Box<dyn FnMut()>) -> Box<dyn StoreUnsubscribe> {
let mut unsubscribes = Vec::with_capacity(self.len());
let callback = Arc::new(Mutex::new(callback));
for signal in self.iter() {
let callback = callback.clone();
unsubscribes.push(signal.subscribe(Box::new(move || callback.lock().unwrap()())));
}
Box::new(VecUnsub { unsubscribes })
}
} }