Skip to content

Commit

Permalink
Refactor and remove ActionResult enum
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Jan 26, 2024
1 parent 957cec7 commit d192b5e
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 27 deletions.
6 changes: 3 additions & 3 deletions crates/concoct-web/src/html.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use concoct::{
hook::{use_context, use_provider, use_ref},
ActionResult, IntoAction, View,
IntoAction, View,
};
use std::{borrow::Cow, cell::RefCell, rc::Rc};
use web_sys::{
Expand Down Expand Up @@ -35,7 +35,7 @@ struct Data<T, A> {
element: Option<Element>,
callbacks: Vec<(
Closure<dyn FnMut(Event)>,
Rc<RefCell<Rc<RefCell<dyn FnMut(&mut T, Event) -> Option<ActionResult<A>>>>>>,
Rc<RefCell<Rc<RefCell<dyn FnMut(&mut T, Event) -> Option<A>>>>>,
)>,
}

Expand All @@ -44,7 +44,7 @@ pub struct Html<C, T, A> {
attrs: Vec<(Cow<'static, str>, Cow<'static, str>)>,
handlers: Vec<(
Cow<'static, str>,
Rc<RefCell<dyn FnMut(&mut T, Event) -> Option<ActionResult<A>>>>,
Rc<RefCell<dyn FnMut(&mut T, Event) -> Option<A>>>,
)>,
content: C,
}
Expand Down
2 changes: 2 additions & 0 deletions crates/concoct/src/hook/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Hooks to access view context.

mod use_context;
pub use self::use_context::use_context;

Expand Down
1 change: 1 addition & 0 deletions crates/concoct/src/hook/use_context.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::Scope;
use std::{any::TypeId, rc::Rc};

/// Hook to get a context from its type.
pub fn use_context<T, A, R: 'static>(cx: &Scope<T, A>) -> Rc<R> {
let contexts = cx.contexts.borrow();
let rc = contexts.get(&TypeId::of::<R>()).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions crates/concoct/src/hook/use_provider.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::use_ref;
use crate::Scope;
use std::{any::TypeId, rc::Rc};

use super::use_ref;

/// Hook to provide a context.
pub fn use_provider<T, A, R: 'static>(cx: &Scope<T, A>, make_initial: impl FnOnce() -> R) {
let value = use_ref(cx, || Rc::new(make_initial()));

Expand Down
7 changes: 7 additions & 0 deletions crates/concoct/src/hook/use_ref.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::Scope;
use std::cell::UnsafeCell;

/// Hook to store a stateless value.
///
/// This function will only call `make_initial` once,
/// on the first render, to create the initial value.
pub fn use_ref<T, A, R: 'static>(cx: &Scope<T, A>, make_initial: impl FnOnce() -> R) -> &mut R {
let mut node = cx.node.inner.borrow_mut();
let idx = node.hook_idx;
Expand All @@ -12,5 +16,8 @@ pub fn use_ref<T, A, R: 'static>(cx: &Scope<T, A>, make_initial: impl FnOnce() -
node.hooks.push(UnsafeCell::new(Box::new(make_initial())));
node.hooks.last().unwrap()
};

// Safety: All references to scope are dropped before `hook_idx` is reset.
// This ensures unique access to each hook.
unsafe { &mut *cell.get() }.downcast_mut().unwrap()
}
37 changes: 21 additions & 16 deletions crates/concoct/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,20 @@ pub mod hook;
pub mod view;
pub use self::view::View;

pub enum ActionResult<A> {
Action(A),
Rebuild,
}

pub struct Handle<T, A = ()> {
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<ActionResult<A>>>)>,
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<A>>)>,
}

impl<T, A> Handle<T, A> {
pub fn update(&self, f: Rc<dyn Fn(&mut T) -> Option<ActionResult<A>>>) {
pub fn update(&self, f: Rc<dyn Fn(&mut T) -> Option<A>>) {
(self.update)(f)
}
}

pub struct Scope<T, A = ()> {
key: DefaultKey,
node: Node,
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<ActionResult<A>>>)>,
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<A>>)>,
is_empty: Cell<bool>,
nodes: Rc<RefCell<SlotMap<DefaultKey, Node>>>,
contexts: RefCell<FxHashMap<TypeId, Rc<dyn Any>>>,
Expand All @@ -81,10 +76,11 @@ struct Node {
}

struct Channel<T> {
updates: Vec<Rc<dyn Fn(&mut T) -> Option<ActionResult<()>>>>,
updates: Vec<Rc<dyn Fn(&mut T) -> Option<()>>>,
waker: Option<Waker>,
}

/// Virtual DOM for a view.
pub struct VirtualDom<T, V> {
content: V,
nodes: Rc<RefCell<SlotMap<DefaultKey, Node>>>,
Expand All @@ -93,6 +89,7 @@ pub struct VirtualDom<T, V> {
}

impl<T, V> VirtualDom<T, V> {
/// Create a new virtual dom from its content.
pub fn new(content: V) -> Self {
Self {
content,
Expand All @@ -105,6 +102,7 @@ impl<T, V> VirtualDom<T, V> {
}
}

/// Build the initial content.
pub fn build(&mut self)
where
T: 'static,
Expand Down Expand Up @@ -132,6 +130,10 @@ impl<T, V> VirtualDom<T, V> {
build_inner(&mut self.content, &cx)
}

/// Rebuild the content from the last build
///
/// ## Panics
/// This function will panic if no initial build has been performed.
pub async fn rebuild(&mut self)
where
T: 'static,
Expand Down Expand Up @@ -254,35 +256,38 @@ where
}
}

/// Marker trait for an action.
pub trait Action {}

/// Convert an output to an optional action.
pub trait IntoAction<A>: sealed::Sealed {
fn into_action(self) -> Option<ActionResult<A>>;
fn into_action(self) -> Option<A>;
}

mod sealed {
pub trait Sealed {}
}

impl sealed::Sealed for () {}

impl<A> IntoAction<A> for () {
fn into_action(self) -> Option<ActionResult<A>> {
fn into_action(self) -> Option<A> {
None
}
}

impl<A: Action> sealed::Sealed for A {}

impl<A: Action> IntoAction<A> for A {
fn into_action(self) -> Option<ActionResult<A>> {
Some(ActionResult::Action(self))
fn into_action(self) -> Option<A> {
Some(self)
}
}

impl<A: Action> sealed::Sealed for Option<ActionResult<A>> {}
impl<A: Action> sealed::Sealed for Option<A> {}

impl<A: Action> IntoAction<A> for Option<ActionResult<A>> {
fn into_action(self) -> Option<ActionResult<A>> {
impl<A: Action> IntoAction<A> for Option<A> {
fn into_action(self) -> Option<A> {
self
}
}
10 changes: 4 additions & 6 deletions crates/concoct/src/view/adapt.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{build_inner, rebuild_inner, ActionResult, Scope, View};
use crate::{build_inner, rebuild_inner, Scope, View};
use std::{cell::RefCell, marker::PhantomData, rc::Rc};

/// Adapt a view's state to a different one.
Expand All @@ -8,8 +8,7 @@ where
A1: 'static,
T2: 'static,
A2: 'static,
F: FnMut(&mut T1, Rc<dyn Fn(&mut T2) -> Option<ActionResult<A2>>>) -> Option<ActionResult<A1>>
+ 'static,
F: FnMut(&mut T1, Rc<dyn Fn(&mut T2) -> Option<A2>>) -> Option<A1> + 'static,
V: View<T2, A2>,
{
Adapt {
Expand All @@ -32,14 +31,13 @@ where
A1: 'static,
T2: 'static,
A2: 'static,
F: FnMut(&mut T1, Rc<dyn Fn(&mut T2) -> Option<ActionResult<A2>>>) -> Option<ActionResult<A1>>
+ 'static,
F: FnMut(&mut T1, Rc<dyn Fn(&mut T2) -> Option<A2>>) -> Option<A1> + 'static,
V: View<T2, A2>,
{
fn body(&mut self, cx: &crate::Scope<T1, A1>) -> impl View<T1, A1> {
let parent_update = cx.update.clone();
let mapper = self.f.clone();
let update = Rc::new(move |f: Rc<dyn Fn(&mut T2) -> Option<ActionResult<A2>>>| {
let update = Rc::new(move |f: Rc<dyn Fn(&mut T2) -> Option<A2>>| {
let mapper = mapper.clone();
parent_update(Rc::new(move |state| mapper.borrow_mut()(state, f.clone())))
});
Expand Down
2 changes: 2 additions & 0 deletions crates/concoct/src/view/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Viewable components.

use crate::{build_inner, hook::use_context, rebuild_inner, Scope, TextViewContext};
use std::{cell::Cell, rc::Rc};

Expand Down

0 comments on commit d192b5e

Please sign in to comment.