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 b1b3b3e
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 422 deletions.
46 changes: 9 additions & 37 deletions examples/app.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,15 @@
use actuate::{use_state, virtual_dom, Scope, View, ViewBuilder};
use actuate::{memo, View};

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)
}
}

struct App;

impl View for App {
fn body(&self, _cx: &Scope) -> impl ViewBuilder {
(Counter { initial: 1 }, Counter { initial: 2 })
}
fn app() -> impl View {
memo(
0,
actuate::from_fn(0, |_| {
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;
}
216 changes: 101 additions & 115 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,148 +1,134 @@
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 {
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 fn from_fn<T, F, V>(state: T, view_fn: F) -> ViewBuilderView<T, F, V>
where
T: 'static,
F: Fn(&T) -> V + 'static,
V: View,
{
ViewBuilderView {
state,
view_fn,
_marker: PhantomData,
}
}

pub struct Context {
nodes: SlotMap<DefaultKey, Node>,
tx: mpsc::UnboundedSender<Update>,
pending_updates: SparseSecondaryMap<DefaultKey, Vec<Update>>,
pub struct ViewBuilderView<T, F, V> {
state: T,
view_fn: F,
_marker: PhantomData<V>,
}

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,
impl<T, F, V> View for ViewBuilderView<T, F, V>
where
T: 'static,
F: Fn(&T) -> V + 'static,
V: View,
{
type State = Option<V::State>;

fn build(&self) -> Self::State {
None
}
}

struct Update {
key: DefaultKey,
idx: usize,
value: Box<dyn Any>,
fn poll_view(&self, cx: &mut Context, state: &mut Self::State) -> Poll<()> {
if let Some(ref mut state) = state {
let body = (self.view_fn)(&self.state);
body.poll_view(cx, state)
} else {
let body = (self.view_fn)(&self.state);
let mut body_state = body.build();

let ret = body.poll_view(cx, &mut body_state);
*state = Some(body_state);
ret
}
}
}

pub struct VirtualDom<T> {
tree: T,
state: Option<Box<dyn Any>>,
cx: Context,
rx: mpsc::UnboundedReceiver<Update>,
roots: Vec<DefaultKey>,
pub fn memo<I, V>(input: I, view: V) -> Memo<I, V>
where
I: PartialEq + Clone,
V: View,
{
Memo { input, 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]);
}
pub struct Memo<I, V> {
input: I,
view: V,
}

// 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]);
}
}
impl<I, V> View for Memo<I, V>
where
I: PartialEq + Clone,
V: View,
{
type State = (I, V::State, bool, bool);

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));
}
fn build(&self) -> Self::State {
(self.input.clone(), self.view.build(), false, false)
}

pub fn slice(&self, key: DefaultKey) -> Slice<T> {
Slice {
vdom: self,
node: self.cx.nodes.get(key).unwrap(),
fn poll_view(&self, cx: &mut Context, state: &mut Self::State) -> Poll<()> {
// Init
if !state.3 {
if self.view.poll_view(cx, &mut state.1).is_ready() {
state.3 = true;
return Poll::Ready(());
} else {
return Poll::Pending;
}
}
}
}

impl<T> fmt::Debug for VirtualDom<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut t = f.debug_tuple("VirtualDom");

for key in &self.roots {
t.field(&self.slice(*key));
if state.2 {
if self.view.poll_view(cx, &mut state.1).is_ready() {
state.2 = false;
Poll::Ready(())
} else {
Poll::Pending
}
} else if self.input != state.0 {
state.0 = self.input.clone();
if self.view.poll_view(cx, &mut state.1).is_ready() {
Poll::Ready(())
} else {
state.2 = true;
Poll::Pending
}
} else {
Poll::Ready(())
}

t.finish()
}
}

pub struct Slice<'a, T> {
vdom: &'a VirtualDom<T>,
node: &'a Node,
}

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 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 b1b3b3e

Please sign in to comment.