Skip to content

Commit

Permalink
Try out async approach
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed May 7, 2024
1 parent e218158 commit e0003f0
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 423 deletions.
41 changes: 6 additions & 35 deletions examples/app.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,14 @@
use actuate::{use_state, virtual_dom, Scope, View, ViewBuilder};

struct A;

impl View for A {
fn body(&self, _cx: &Scope) -> impl ViewBuilder {
dbg!("A!");
}
}

struct Counter {
initial: i32,
}

impl View for Counter {
fn body(&self, cx: &Scope) -> impl ViewBuilder {
let (count, set_count) = use_state(cx, || self.initial);

set_count.set(count + 1);

dbg!(count);

(*count == 2).then_some(A)
}
}
use actuate::ViewBuilder;

struct App;

impl View for App {
fn body(&self, _cx: &Scope) -> impl ViewBuilder {
(Counter { initial: 1 }, Counter { initial: 2 })
impl ViewBuilder for App {
fn body(&self) -> impl actuate::IntoView {
dbg!("Hello World!");
}
}

#[tokio::main]
async fn main() {
let mut vdom = virtual_dom(App);

vdom.run().await;
vdom.run().await;

dbg!(vdom);
}
actuate::run(App).await;
}
173 changes: 55 additions & 118 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,148 +1,85 @@
use slotmap::{DefaultKey, SlotMap, SparseSecondaryMap};
use std::fmt;
use std::{any::Any, cell::UnsafeCell};
use tokio::sync::mpsc;
use std::{
future,
marker::PhantomData,
mem,
task::{Context, Poll},
};

mod use_state;
pub use self::use_state::{use_state, Setter};
pub trait View: 'static {
type State;

mod tree;
pub use self::tree::{Tree, ViewTree};
fn build(&self) -> Self::State;

mod view;
pub use self::view::View;

mod view_builder;
pub use self::view_builder::ViewBuilder;

struct Inner {
hooks: Vec<Box<dyn Any>>,
idx: usize,
fn poll_view(&self, cx: &mut Context, state: &mut Self::State) -> Poll<()>;
}

pub struct Scope {
key: DefaultKey,
inner: UnsafeCell<Inner>,
tx: mpsc::UnboundedSender<Update>,
}
impl View for () {
type State = ();

trait AnyView {
fn name(&self) -> &'static str;
}
fn build(&self) -> Self::State {}

impl<VB: ViewBuilder> AnyView for VB {
fn name(&self) -> &'static str {
std::any::type_name::<VB>()
fn poll_view(&self, cx: &mut Context, state: &mut Self::State) -> Poll<()> {
Poll::Ready(())
}
}

struct Node {
view: *const dyn AnyView,
children: Vec<DefaultKey>,
}

pub struct Context {
nodes: SlotMap<DefaultKey, Node>,
tx: mpsc::UnboundedSender<Update>,
pending_updates: SparseSecondaryMap<DefaultKey, Vec<Update>>,
}

pub fn virtual_dom(view: impl ViewBuilder) -> VirtualDom<impl Tree> {
let (tx, rx) = mpsc::unbounded_channel();

VirtualDom {
tree: view.into_tree(),
state: None,
cx: Context {
nodes: SlotMap::new(),
tx,
pending_updates: SparseSecondaryMap::new(),
},
roots: Vec::new(),
rx,
}
pub trait ViewBuilder: 'static {
fn body(&self) -> impl IntoView;
}

struct Update {
key: DefaultKey,
idx: usize,
value: Box<dyn Any>,
pub trait IntoView {
fn into_view(self) -> impl View;
}

pub struct VirtualDom<T> {
tree: T,
state: Option<Box<dyn Any>>,
cx: Context,
rx: mpsc::UnboundedReceiver<Update>,
roots: Vec<DefaultKey>,
impl IntoView for () {
fn into_view(self) -> impl View {}
}

impl<T> VirtualDom<T> {
pub async fn run(&mut self)
where
T: Tree,
{
if let Some(ref mut state) = self.state {
// Wait for at least one update.
let update = self.rx.recv().await.unwrap();

if let Some(updates) = self.cx.pending_updates.get_mut(update.key) {
updates.push(update);
} else {
self.cx.pending_updates.insert(update.key, vec![update]);
}

// Flush any pending updates.
while let Ok(update) = self.rx.try_recv() {
if let Some(updates) = self.cx.pending_updates.get_mut(update.key) {
updates.push(update);
} else {
self.cx.pending_updates.insert(update.key, vec![update]);
}
}

self.tree
.rebuild(&mut self.cx, state.downcast_mut().unwrap(), &mut self.roots)
} else {
let state = self.tree.build(&mut self.cx, &mut self.roots);
self.state = Some(Box::new(state));
impl<VB: ViewBuilder> IntoView for VB {
fn into_view(self) -> impl View {
ViewBuilderView {
view_builder: self,
view_fn: |me: &Self| {
let me: &'static Self = unsafe { mem::transmute(me) };
me.body().into_view()
},
_marker: PhantomData,
}
}
}

pub fn slice(&self, key: DefaultKey) -> Slice<T> {
Slice {
vdom: self,
node: self.cx.nodes.get(key).unwrap(),
}
}
pub struct ViewBuilderView<VB, B, F> {
view_builder: VB,
view_fn: F,
_marker: PhantomData<B>,
}

impl<T> fmt::Debug for VirtualDom<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut t = f.debug_tuple("VirtualDom");
impl<VB, B, F> View for ViewBuilderView<VB, B, F>
where
VB: ViewBuilder,
B: View,
F: Fn(&VB) -> B + 'static,
{
type State = B::State;

for key in &self.roots {
t.field(&self.slice(*key));
}
fn build(&self) -> Self::State {
let body = (self.view_fn)(&self.view_builder);
let body_state = body.build();

t.finish()
body_state
}
}

pub struct Slice<'a, T> {
vdom: &'a VirtualDom<T>,
node: &'a Node,
fn poll_view(&self, cx: &mut Context, state: &mut Self::State) -> Poll<()> {
let body = (self.view_fn)(&self.view_builder);
body.poll_view(cx, state)
}
}

impl<T> fmt::Debug for Slice<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let view = unsafe { &*self.node.view };
let mut t = f.debug_tuple(view.name());

for child_key in &self.node.children {
t.field(&self.vdom.slice(*child_key));
}
pub async fn run(view: impl IntoView) {
let view = view.into_view();
let mut state = view.build();

t.finish()
loop {
future::poll_fn(|cx| view.poll_view(cx, &mut state)).await
}
}
Loading

0 comments on commit e0003f0

Please sign in to comment.