From d3cc64375ac1fd4db8f5af097b2953c0f09d7ddd Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Sun, 25 Feb 2024 21:33:01 +0100 Subject: [PATCH] switch to signals, start working on slots --- Cargo.lock | 85 +++-- mangades/src/component_demo_syntax.rs | 9 +- mangades/src/main.rs | 1 + mangades/src/slot_demo.rs | 266 +++++++++++++++ rusalka-macro/Cargo.toml | 1 + rusalka-macro/src/lib.rs | 466 ++++++++++---------------- rusalka/src/component.rs | 23 +- rusalka/src/invalidator.rs | 36 -- rusalka/src/lib.rs | 1 - rusalka/src/nodes/mod.rs | 2 - rusalka/src/nodes/primitives.rs | 91 ----- rusalka/src/store/mod.rs | 154 +++++++-- 12 files changed, 619 insertions(+), 516 deletions(-) create mode 100644 mangades/src/slot_demo.rs delete mode 100644 rusalka/src/invalidator.rs delete mode 100644 rusalka/src/nodes/primitives.rs diff --git a/Cargo.lock b/Cargo.lock index da95466..3f26027 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" @@ -213,11 +213,10 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" dependencies = [ - "jobserver", "libc", ] @@ -858,9 +857,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "http" @@ -1022,15 +1021,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -1124,9 +1114,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" [[package]] name = "mangades" @@ -1362,9 +1352,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.100" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae94056a791d0e1217d18b6cbdccb02c61e3054fc69893607f4067e3bb0b1fd1" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -1640,6 +1630,7 @@ dependencies = [ name = "rusalka-macro" version = "0.1.0" dependencies = [ + "proc-macro2", "quote", ] @@ -1906,12 +1897,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2558,7 +2549,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -2593,17 +2584,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -2620,9 +2611,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -2638,9 +2629,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -2656,9 +2647,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -2674,9 +2665,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -2692,9 +2683,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -2710,9 +2701,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -2728,9 +2719,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winit" diff --git a/mangades/src/component_demo_syntax.rs b/mangades/src/component_demo_syntax.rs index 8eaa8ce..b4ca708 100644 --- a/mangades/src/component_demo_syntax.rs +++ b/mangades/src/component_demo_syntax.rs @@ -5,12 +5,9 @@ use mangui::nodes::TaffyStyle; use mangui::taffy::Display::Block; use mangui::taffy::{FlexDirection, LengthPercentage, Rect}; -use rusalka::nodes::primitives::{Rectangle, RectangleAttributes, PartialRectangleAttributes}; - make_component!( ComponentDemo, MainLogic { - let _radius = attrs.radius; let imgpath = std::path::PathBuf::from("./demo/large/bx117324-97mHyfJGwpBq.jpg"); let imgflags = ImageFlags::empty(); let width = 230.; @@ -23,13 +20,11 @@ make_component!( test_: bool = false } Reactive { - // dbg!($test_); + println!("reactive block"); + println!("test_ = {}", $test_); } Component { @layout { - @Rectangle { - radius: if $test_ { attrs.radius } else { 0. } - } @layout { @text { text: String::from("Hello, World 🌎! And there's more text in here, as a single line"), diff --git a/mangades/src/main.rs b/mangades/src/main.rs index 20babe6..1949cb1 100644 --- a/mangades/src/main.rs +++ b/mangades/src/main.rs @@ -4,6 +4,7 @@ use mangui::{self, nodes::layout::Layout, SharedNode, MainEntry}; mod component_demo_syntax; mod anilist; +mod slot_demo; use rusalka::component::Component; diff --git a/mangades/src/slot_demo.rs b/mangades/src/slot_demo.rs new file mode 100644 index 0000000..e80b948 --- /dev/null +++ b/mangades/src/slot_demo.rs @@ -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, + comp1: Mutex>, + selfref: rusalka::WeakSharedComponent, + attrs: ReactiveSlotAcceptDemoAttributes, +} + +type SlotArgs = (); +type DSlot = Option 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 +} +impl From for PartialSlotAcceptDemoAttributes { + fn from(attrs: SlotAcceptDemoAttributes) -> Self { + Self { __default_slot: Some(attrs.__default_slot) } + } +} + +impl From 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 { + 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, + sub0: Box +} + +pub struct SlotDemo { + comp0: rusalka::SharedNodeComponent, + comp1: rusalka::SharedComponent, + test_: std::sync::Arc>>, + sub0: Box, + selfref: rusalka::WeakSharedComponent, + attrs: ReactiveSlotDemoAttributes, +} +#[derive(Default)] +pub struct SlotDemoAttributes { + pub test: f32 +} + +pub struct ReactiveSlotDemoAttributes { + pub test: Arc>> +} + +#[derive(Default)] +pub struct PartialSlotDemoAttributes { + pub test: Option +} +impl From for PartialSlotDemoAttributes { + fn from(attrs: SlotDemoAttributes) -> Self { + Self { test: Some(attrs.test) } + } +} + +impl From 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 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 { + let attrs: Self::ReactiveComponentAttrs = attrs.into(); + let test_: std::sync::Arc< + std::sync::Mutex>, + > = 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>> = 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() }); + } +} \ No newline at end of file diff --git a/rusalka-macro/Cargo.toml b/rusalka-macro/Cargo.toml index 22b90e4..f0e804b 100644 --- a/rusalka-macro/Cargo.toml +++ b/rusalka-macro/Cargo.toml @@ -8,6 +8,7 @@ authors.workspace = true [dependencies] quote = "1.0" +proc-macro2 = "1.0.78" [lib] proc-macro = true \ No newline at end of file diff --git a/rusalka-macro/src/lib.rs b/rusalka-macro/src/lib.rs index 09cb936..9c35722 100644 --- a/rusalka-macro/src/lib.rs +++ b/rusalka-macro/src/lib.rs @@ -1,7 +1,7 @@ +use proc_macro2::{Ident, TokenStream, TokenTree, Span, Group, Delimiter}; use std::collections::HashMap; -use proc_macro::{TokenStream, TokenTree, Ident, Group, Span, Literal}; -use quote::quote; +use quote::{format_ident, quote, ToTokens}; #[derive(Debug, Clone)] struct Attribute { @@ -51,7 +51,8 @@ struct ReactiveBlock { #[proc_macro] /// 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 item = item.into_iter(); let name = item.next().unwrap(); @@ -201,11 +202,13 @@ pub fn make_component(item: TokenStream) -> TokenStream { }, "Reactive" => { let (variables, contents) = replace_variables(group.stream()); - reactive_blocks.push(ReactiveBlock { - variables, - contents, - prop_ident: None - }); + if !contents.is_empty() && !variables.is_empty() { + reactive_blocks.push(ReactiveBlock { + variables, + contents, + prop_ident: None + }); + } }, "Component" => { // Example syntax: @@ -268,25 +271,28 @@ pub fn make_component(item: TokenStream) -> TokenStream { let mut output = TokenStream::new(); // 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()))); let mut component_struct_stream = TokenStream::new(); - let mut i = 0; - for component in &components_used { - + for (i, component) in components_used.iter().enumerate() { 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 { ComponentType::RealComponent => { - midstream.extend(TokenStream::from(quote!(SharedComponent<))); + midstream.extend(quote!(SharedComponent<)); }, 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(midstream); 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, + )); i+=1; } 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>>))); component_struct_stream.extend(TokenStream::from(quote!(,))); @@ -316,11 +329,13 @@ pub fn make_component(item: TokenStream) -> TokenStream { // Attributes struct 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(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))); @@ -338,11 +353,10 @@ pub fn make_component(item: TokenStream) -> TokenStream { 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 - 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(Some(TokenTree::Ident(partial_attributes_ident.clone()))); 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!(>,))); } - 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>>,))); + } + + output.extend(Some(TokenTree::Group(Group::new(proc_macro2::Delimiter::Brace, attributes_default_struct_stream)))); // impl From for PartialAttributes @@ -371,7 +401,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { 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(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))); 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(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)))); - output.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Brace, from_stream)))); + from_stream.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_fn_stream)))); + output.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, from_stream)))); + + // impl From 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 - output.extend(TokenStream::from(quote!(impl rusalka::component::Component for))); - output.extend(Some(TokenTree::Ident(name_ident.clone()))); + output.extend(TokenStream::from(quote!(impl rusalka::component::Component for #name_ident))); let mut component_impl_stream = TokenStream::new(); - component_impl_stream.extend(TokenStream::from(quote!(type ComponentAttrs =))); - component_impl_stream.extend(Some(TokenTree::Ident(attributes_ident.clone()))); - component_impl_stream.extend(TokenStream::from(quote!(;))); - component_impl_stream.extend(TokenStream::from(quote!(type PartialComponentAttrs =))); - 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!(;))); + component_impl_stream.extend(TokenStream::from(quote!( + type ComponentAttrs = #attributes_ident; + type PartialComponentAttrs = #partial_attributes_ident; + type ReactiveComponentAttrs = #reactive_attributes_ident; + ))); // fn new - component_impl_stream.extend(TokenStream::from(quote!(fn new(attrs: Self::ComponentAttrs, selfref: rusalka::WeakSharedComponent) -> Self))); + component_impl_stream.extend(TokenStream::from(quote!( + fn new(attrs: Self::ComponentAttrs, selfref: rusalka::WeakSharedComponent) -> Self + ))); let mut new_stream = TokenStream::new(); - for variable in &reactive_variables { - new_stream.extend(TokenStream::from(quote!(let))); - new_stream.extend(Some(TokenTree::Ident(variable.name.clone()))); - new_stream.extend(TokenStream::from(quote!(:))); - new_stream.extend(TokenStream::from(quote!(std::sync::Arc>>))); - new_stream.extend(TokenStream::from(quote!(=))); + new_stream.extend(TokenStream::from(quote!( + let attrs: Self::ReactiveComponentAttrs = attrs.into(); + ))); - 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(); if let Some(def) = &variable.default { invalidator_inner.extend(replace_variables(def.clone()).1); } else { invalidator_inner.extend(TokenStream::from(quote!(Default::default()))); } - invalidator.extend(Some(TokenTree::Group(Group::new(proc_macro::Delimiter::Parenthesis, invalidator_inner)))); - new_stream.extend(wrap_in_arc_mutex(invalidator)); - - new_stream.extend(TokenStream::from(quote!(;))); + new_stream.extend(TokenStream::from(quote!( + let #name : std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(rusalka::store::Writable::new( #invalidator_inner ))); + ))); } new_stream.extend(main_logic); @@ -466,20 +530,20 @@ pub fn make_component(item: TokenStream) -> TokenStream { 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 - // 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(TokenStream::from(quote!(as Component>::ComponentAttrs))); 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 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(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))); @@ -487,27 +551,44 @@ pub fn make_component(item: TokenStream) -> TokenStream { }, ComponentType::Node => { 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))); new_returnvalue_stream.extend(wrap_in_arcrwlock(component_stream)); } } - new_returnvalue_stream.extend(TokenStream::from(quote!(,))); + new_returnvalue_stream.extend(quote!(,)); 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 { 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(TokenStream::from(quote!(;))); + new_stream.extend(quote!(;)); i = 0; 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(TokenStream::from(quote!(this.tick(None);))); - - let callback_group = Group::new(proc_macro::Delimiter::Brace, inner_callback_stream); + let callback_group = Group::new(Delimiter::Brace, inner_callback_stream); 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))); - 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))); @@ -572,7 +651,7 @@ pub fn make_component(item: TokenStream) -> TokenStream { 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))); // fn set @@ -581,14 +660,13 @@ pub fn make_component(item: TokenStream) -> TokenStream { let mut set_stream = TokenStream::new(); 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() { set_stream.extend(TokenStream::from(quote!(if let Some))); let mut some_inner = TokenStream::new(); 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(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(Some(TokenTree::Ident(attribute.name.clone()))); - set_stream_inner.extend(TokenStream::from(quote!(=))); - set_stream_inner.extend(Some(TokenTree::Ident(attribute.name.clone()))); - set_stream_inner.extend(TokenStream::from(quote!(; to_update))); + set_stream_inner.extend(TokenStream::from(quote!(.lock().unwrap().set))); - 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_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(Some(TokenTree::Group(Group::new(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 - 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 @@ -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(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(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!(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))); @@ -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(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(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(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(TokenStream::from(quote!(,))); 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(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))); // 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(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))); - 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(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))); - // fn update - - 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 = 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 = 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)))); + output.extend(Some(TokenTree::Group(Group::new(Delimiter::Brace, component_impl_stream)))); println!("{}", output.to_string()); 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 /// Returned vec is sorted and deduplicated fn replace_variables(stream: TokenStream) -> (Vec, TokenStream) { @@ -987,7 +869,7 @@ fn replace_variables(stream: TokenStream) -> (Vec, TokenStream) { idents.push(ident.clone()); output.extend(TokenStream::from(quote!(**))); 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) => { 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))); - let mutex_group = Group::new(proc_macro::Delimiter::Parenthesis, stream); + let mutex_group = Group::new(Delimiter::Parenthesis, stream); 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))); - 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))); @@ -1035,7 +917,7 @@ fn wrap_in_arcmutex_cyclic(stream: TokenStream) -> TokenStream { 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(); @@ -1045,7 +927,7 @@ fn wrap_in_arcmutex_cyclic(stream: TokenStream) -> TokenStream { 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))); @@ -1057,7 +939,7 @@ fn wrap_in_arcrwlock(stream: TokenStream) -> TokenStream { 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(); @@ -1065,7 +947,7 @@ fn wrap_in_arcrwlock(stream: TokenStream) -> TokenStream { 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))); diff --git a/rusalka/src/component.rs b/rusalka/src/component.rs index a14f535..cdc2446 100644 --- a/rusalka/src/component.rs +++ b/rusalka/src/component.rs @@ -4,25 +4,18 @@ use crate::WeakSharedComponent; /// A rusalka component pub trait Component { - type ComponentAttrs: Default; + type ComponentAttrs; + type ReactiveComponentAttrs: From; type PartialComponentAttrs: Default + From; const UPDATE_LENGTH : usize = 0; fn new(attr: Self::ComponentAttrs, selfref: WeakSharedComponent) -> Self; - fn get(&self) -> &Self::ComponentAttrs; + fn get(&self) -> &Self::ReactiveComponentAttrs; fn set(&mut self, attr: Self::PartialComponentAttrs); fn mount(&self, parent: &SharedNode, before: Option<&SharedNode>); - fn update(&self, bitmap: &[u32]); 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); - - 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)>, + pub unmount: Box, +} \ No newline at end of file diff --git a/rusalka/src/invalidator.rs b/rusalka/src/invalidator.rs deleted file mode 100644 index 9d86091..0000000 --- a/rusalka/src/invalidator.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::ops::{Deref, DerefMut}; - -pub struct Invalidator { - inner: T, - invalidated: bool -} - -impl Invalidator { - 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 Deref for Invalidator { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl DerefMut for Invalidator { - fn deref_mut(&mut self) -> &mut Self::Target { - self.invalidated = true; - &mut self.inner - } -} - diff --git a/rusalka/src/lib.rs b/rusalka/src/lib.rs index c21f762..97af669 100644 --- a/rusalka/src/lib.rs +++ b/rusalka/src/lib.rs @@ -5,7 +5,6 @@ use mangui::nodes::Node; pub mod component; pub mod nodes; -pub mod invalidator; pub mod store; pub type SharedComponent = Arc>; diff --git a/rusalka/src/nodes/mod.rs b/rusalka/src/nodes/mod.rs index 009eabc..e5dad21 100644 --- a/rusalka/src/nodes/mod.rs +++ b/rusalka/src/nodes/mod.rs @@ -1,8 +1,6 @@ use std::sync::Arc; use mangui::SharedNode; -pub mod primitives; - pub fn detach(node: &SharedNode) { if let Some(parent) = node.read().unwrap().parent() { parent.write().unwrap().remove_child(node).unwrap(); diff --git a/rusalka/src/nodes/primitives.rs b/rusalka/src/nodes/primitives.rs deleted file mode 100644 index b3c44af..0000000 --- a/rusalka/src/nodes/primitives.rs +++ /dev/null @@ -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>, - attrs: RectangleAttributes, - selfref: WeakSharedComponent -} - -#[derive(Default)] -pub struct RectangleAttributes { - pub radius: f32 -} - -#[derive(Default)] -pub struct PartialRectangleAttributes { - pub radius: Option -} - -impl From 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 { - 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()}); - } -} \ No newline at end of file diff --git a/rusalka/src/store/mod.rs b/rusalka/src/store/mod.rs index d81b2c9..c0b5a02 100644 --- a/rusalka/src/store/mod.rs +++ b/rusalka/src/store/mod.rs @@ -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 pub trait StoreUnsubscribe: Drop {} -pub trait ReadableStore { +pub trait Signal { + fn subscribe(&self, callback: Box) -> Box; +} + +pub trait ReadableStore: Signal { type State; fn get(&self) -> &Self::State; - fn subscribe(&self, callback: Box ()>) -> Box; } pub trait WritableStore: ReadableStore { fn set(&mut self, state: Self::State); } -struct Listener { +struct Listener { hash: u64, - callback: Box ()> + callback: Box } - -struct ReadableUnsubscribe { - listeners: Weak>>>, +struct ReadableUnsubscribe { + listeners: Weak>>, hash: u64 } -impl Drop for ReadableUnsubscribe { +impl Drop for ReadableUnsubscribe { fn drop(&mut self) { if let Some(listeners) = self.listeners.upgrade() { let mut listeners = listeners.lock().unwrap(); @@ -33,11 +36,11 @@ impl Drop for ReadableUnsubscribe { } } -impl StoreUnsubscribe for ReadableUnsubscribe {} +impl StoreUnsubscribe for ReadableUnsubscribe {} pub struct Readable { state: T, - listeners: Arc>>> + listeners: Arc>> } impl Readable { @@ -51,14 +54,11 @@ impl Readable { static CALL_COUNT: AtomicU64 = AtomicU64::new(0); -impl ReadableStore for Readable { - type State = T; - fn get(&self) -> &Self::State { - &self.state - } - fn subscribe(&self, callback: Box ()>) -> Box { +impl Signal for Readable { + fn subscribe(&self, mut callback: Box) -> Box { 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 @@ -70,9 +70,16 @@ impl ReadableStore for Readable { } } +impl ReadableStore for Readable { + type State = T; + fn get(&self) -> &Self::State { + &self.state + } +} + pub struct Writable { state: T, - listeners: Arc>>> + listeners: Arc>> } impl Writable { @@ -84,14 +91,11 @@ impl Writable { } } -impl ReadableStore for Writable { - type State = T; - fn get(&self) -> &Self::State { - &self.state - } - fn subscribe(&self, callback: Box ()>) -> Box { +impl Signal for Writable { + fn subscribe(&self, mut callback: Box) -> Box { 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 @@ -103,12 +107,112 @@ impl ReadableStore for Writable { } } +impl ReadableStore for Writable { + type State = T; + fn get(&self) -> &Self::State { + &self.state + } +} + impl WritableStore for Writable { fn set(&mut self, state: Self::State) { self.state = state; let mut listeners = self.listeners.lock().unwrap(); for listener in listeners.iter_mut() { - (listener.callback)(&self.state); + (listener.callback)(); } } +} + +impl Default for Writable where T: Default { + fn default() -> Self { + Self::new(Default::default()) + } +} + +pub struct DerefGuard { + listeners: Arc>>, + tainted: bool, + inner: T +} + +pub trait DerefGuardExt: WritableStore + Signal { + fn guard(&mut self) -> DerefGuard<&mut T>; +} + +impl DerefGuardExt for Writable { + fn guard(&mut self) -> DerefGuard<&mut T> { + DerefGuard { + listeners: self.listeners.clone(), + tainted: false, + inner: &mut self.state + } + } +} + +impl Deref for DerefGuard { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for DerefGuard { + fn deref_mut(&mut self) -> &mut Self::Target { + self.tainted = true; + &mut self.inner + } +} + +impl Drop for DerefGuard { + 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> +} +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 Signal for MutexGuard<'_, T> { + fn subscribe(&self, callback: Box) -> Box { + self.deref().subscribe(callback) + } +} + +impl Signal for Vec { + /// Subscribes to all signals in the vector + fn subscribe(&self, callback: Box) -> Box { + 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 Signal for [T] { + /// Subscribes to all signals in the array + fn subscribe(&self, callback: Box) -> Box { + 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 }) + } } \ No newline at end of file