wip: refactor a bit

This commit is contained in:
Jonathan Kelley committed 2021-04-04 21:47:53 -04:00
1 parent 8c541f66d5
commit 2eeb8f2386
9 files changed
+553 -297

No files matched your search

+11 -14
View File
@@ -1,4 +1,4 @@
use crate::{nodebuilder::IntoDomTree, prelude::*};
use crate::{nodebuilder::IntoDomTree, prelude::*, scope::Scope};
use crate::{nodebuilder::LazyNodes, nodes::VNode};
use bumpalo::Bump;
use hooks::Hook;
@@ -27,15 +27,12 @@ use std::{cell::RefCell, future::Future, ops::Deref, pin::Pin, rc::Rc, sync::ato
pub struct Context<'src> {
pub idx: RefCell<usize>,
pub scope: ScopeIdx,
// Borrowed from scope
// pub(crate) arena: &'src typed_arena::Arena<Hook>,
pub(crate) hooks: &'src RefCell<Vec<Hook>>,
// pub(crate) hooks: &'src RefCell<Vec<*mut Hook>>,
pub(crate) bump: &'src Bump,
pub listeners: &'src RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
// pub scope: ScopeIdx,
pub scope: &'src Scope,
// // Borrowed from scope
// pub(crate) hooks: &'src RefCell<Vec<Hook>>,
// pub(crate) bump: &'src Bump,
// pub listeners: &'src RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
// holder for the src lifetime
// todo @jon remove this
@@ -101,10 +98,10 @@ impl<'a> Context<'a> {
lazy_nodes: LazyNodes<'a, F>,
) -> DomTree {
let ctx = NodeCtx {
bump: self.bump,
scope: self.scope,
bump: &self.scope.cur_frame().bump,
scope: self.scope.myidx,
idx: 0.into(),
listeners: self.listeners,
listeners: &self.scope.listeners,
};
let safe_nodes = lazy_nodes.into_vnode(&ctx);
@@ -145,7 +142,7 @@ pub mod hooks {
let idx = *self.idx.borrow();
// Mutate hook list if necessary
let mut hooks = self.hooks.borrow_mut();
let mut hooks = self.scope.hooks.borrow_mut();
// Initialize the hook by allocating it in the typed arena.
// We get a reference from the arena which is owned by the component scope
+39 -13
View File
@@ -44,7 +44,7 @@ pub struct Scope {
// - is self-refenrential and therefore needs to point into the bump
// Stores references into the listeners attached to the vnodes
// NEEDS TO BE PRIVATE
listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
pub(crate) listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
}
impl Scope {
@@ -85,21 +85,26 @@ impl Scope {
/// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
pub fn run_scope<'b>(&'b mut self) -> Result<()> {
// pub fn run_scope<'bump>(&'bump mut self) -> Result<()> {
let frame = {
// let frame = {
{
let frame = self.frames.next();
frame.bump.reset();
frame
};
}
// frame
// };
// self.new_frame()
let ctx: Context<'b> = Context {
let ctx = Context {
// arena: &self.hook_arena,
hooks: &self.hooks,
bump: &frame.bump,
// hooks: &self.hooks,
// bump: &frame.bump,
idx: 0.into(),
_p: PhantomData {},
scope: self.myidx,
listeners: &self.listeners,
scope: self,
// scope: self.myidx,
// listeners: &self.listeners,
};
let caller = self.caller.upgrade().expect("Failed to get caller");
/*
@@ -114,12 +119,16 @@ impl Scope {
- Public API cannot drop or destructure VNode
*/
frame.head_node = unsafe {
let caller2: Rc<OpaqueComponent<'b>> = std::mem::transmute(caller);
let tree = (caller2.as_ref())(ctx);
tree.root
let new_head = unsafe {
// frame.head_node = unsafe {
// // use the same type, just manipulate the lifetime
type ComComp<'c> = Rc<OpaqueComponent<'c>>;
let caller = std::mem::transmute::<ComComp<'static>, ComComp<'b>>(caller);
let r: DomTree = (caller.as_ref())(ctx);
r
};
self.frames.cur_frame_mut().head_node = new_head.root;
Ok(())
}
@@ -161,6 +170,10 @@ impl Scope {
pub fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
self.frames.prev_head_node()
}
pub fn cur_frame(&self) -> &BumpFrame {
self.frames.cur_frame()
}
}
// ==========================
@@ -203,6 +216,19 @@ impl ActiveFrame {
}
}
fn cur_frame(&self) -> &BumpFrame {
match *self.idx.borrow() & 1 == 0 {
true => &self.frames[0],
false => &self.frames[1],
}
}
fn cur_frame_mut(&mut self) -> &mut BumpFrame {
match *self.idx.borrow() & 1 == 0 {
true => &mut self.frames[0],
false => &mut self.frames[1],
}
}
pub fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
let raw_node = match *self.idx.borrow() & 1 == 0 {
true => &self.frames[0],
+19 -3
View File
@@ -27,7 +27,7 @@ pub struct VirtualDom {
/// Will not be ready if the dom is fresh
pub base_scope: ScopeIdx,
pub(crate) update_schedule: Rc<RefCell<Vec<HierarchyMarker>>>,
pub(crate) update_schedule: UpdateFunnel,
// a strong allocation to the "caller" for the original props
#[doc(hidden)]
@@ -74,7 +74,7 @@ impl VirtualDom {
components,
_root_caller: root_caller,
base_scope,
update_schedule: Rc::new(RefCell::new(Vec::new())),
update_schedule: UpdateFunnel::default(),
_root_prop_type: TypeId::of::<P>(),
}
}
@@ -211,5 +211,21 @@ impl VirtualDom {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct HierarchyMarker {
height: u32,
source: ScopeIdx,
}
#[derive(Debug, Default, PartialEq, Clone)]
pub struct UpdateFunnel(Rc<RefCell<Vec<HierarchyMarker>>>);
impl UpdateFunnel {
fn schedule_update(&self, source: ScopeIdx) -> impl Fn() {
let inner = self.clone();
move || {
inner
.0
.as_ref()
.borrow_mut()
.push(HierarchyMarker { source })
}
}
}
-218
View File
@@ -1,218 +0,0 @@
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
use recoil::{use_recoil_callback, RecoilContext};
use uuid::Uuid;
static TODOS: AtomFamily<Uuid, TodoItem> = atom_family(|_| {});
fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, props| {
let global_reducer = use_recoil_callback(|| ());
let todos = use_atom(TODOS).iter().map(|(order, item)| {
rsx!(TodoItem {
key: "{order}",
id: item.id,
})
});
ctx.render(rsx! {
div {
{todos}
Footer {}
}
})
}))
}
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: Uuid,
pub checked: bool,
pub contents: String,
}
// build a global context for the app
// as we scale the app, we can create separate, stateful impls
impl RecoilContext<()> {
fn add_todo(&self) {}
fn remove_todo(&self) {}
fn select_all_todos(&self) {}
}
mod hooks {
use super::*;
fn use_keyboard_shortcuts(ctx: &Context) {}
}
// The data model that the todo mvc uses
mod model {
use std::{borrow::BorrowMut, future::Future};
use super::*;
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: Uuid,
pub checked: bool,
pub contents: String,
}
fn atom() {}
// struct Dispatcher {}
struct AppContext<T: Clone> {
_t: std::rc::Rc<T>,
}
// pub fn use_appcontext<T: Clone>(ctx: &Context, f: impl FnOnce() -> T) -> AppContext<T> {
// todo!()
// }
// static TodoList: ContextFamily = context_family();
// struct TodoBoss<'a> {}
// fn use_recoil_todos() -> TodoBoss {}
// pub fn use_context_family(ctx: &Context) {}
impl<T: Clone> AppContext<T> {
fn dispatch(&self, f: impl FnOnce(&mut T)) {}
fn async_dispatch(&self, f: impl Future<Output = ()>) {}
fn get<G>(&self, f: impl Fn(&T) -> &G) -> &G {
f(&self._t)
}
fn set(&self, orig: &mut std::borrow::Cow<T>) {
let r = orig.to_mut();
}
}
// // use im-rc if your contexts are too large to clone!
// // or, dangerously mutate and update subscriptions manually
// #[derive(Clone, Debug, PartialEq)]
// pub struct TodoManager {
// items: Vec<u32>,
// }
// // App context is an ergonomic way of sharing data models through a tall tree
// // Because it holds onto the source data with Rc, it's cheap to clone through props and allows advanced memoization
// // It's particularly useful when moving through tall trees, or iterating through complex data models.
// // By wrapping the source type, we can forward any mutation through "dispatch", making it clear when clones occur.
// // This also enables traditional method-style
// impl AppContext<TodoManager> {
// fn get_todos(&self, ctx: &Context) {}
// fn remove_todo(&self, id: Uuid) {
// self.dispatch(|f| {
// // todos... remove
// })
// }
// async fn push_todo(&self, todo: TodoItem) {
// self.dispatch(|f| {
// //
// f.items.push(10);
// });
// }
// fn add_todo(&self) {
// // self.dispatch(|f| {});
// // let items = self.get(|f| &f.items);
// }
// }
// pub enum TodoActions {}
// impl TodoManager {
// pub fn reduce(s: &mut Rc<Self>, action: TodoActions) {
// match action {
// _ => {}
// }
// }
// pub fn new() -> Rc<Self> {
// todo!()
// }
// pub fn get_todo(&self, id: Uuid) -> &TodoItem {
// todo!()
// }
// pub fn get_todos(&self) -> &BTreeMap<String, TodoItem> {
// todo!()
// }
// }
// pub struct TodoHandle {}
// impl TodoHandle {
// fn get_todo(&self, id: Uuid) -> &TodoItem {
// todo!()
// }
// fn add_todo(&self, todo: TodoItem) {}
// }
// // use_reducer, but exposes the reducer and context to children
// fn use_reducer_context() {}
// fn use_context_selector() {}
// fn use_context<'b, 'c, Root: 'static, Item: 'c>(
// ctx: &'b Context<'c>,
// f: impl Fn(Root) -> &'c Item,
// ) -> &'c Item {
// todo!()
// }
// pub fn use_todo_item<'b, 'c>(ctx: &'b Context<'c>, item: Uuid) -> &'c TodoItem {
// todo!()
// // ctx.use_hook(|| TodoManager::new(), |hook| {}, cleanup)
// }
// fn use_todos(ctx: &Context) -> TodoHandle {
// todo!()
// }
// fn use_todo_context(ctx: &Context) -> AppContext<TodoManager> {
// todo!()
// }
// fn test(ctx: Context) {
// let todos = use_todos(&ctx);
// let todo = todos.get_todo(Uuid::new_v4());
// let c = use_todo_context(&ctx);
// // todos.add_todo();
// }
}
mod recoil {
pub struct RecoilContext<T: 'static> {
_inner: T,
}
impl<T: 'static> RecoilContext<T> {
/// Get the value of an atom. Returns a reference to the underlying data.
pub fn get(&self) {}
/// Replace an existing value with a new value
///
/// This does not replace the value instantly, and all calls to "get" within the current scope will return
pub fn set(&self) {}
// Modify lets you modify the value in place. However, because there's no previous value around to compare
// the new one with, we are unable to memoize the change. As such, all downsteam users of this Atom will
// be updated, causing all subsrcibed components to re-render.
//
// This is fine for most values, but might not be performant when dealing with collections. For collections,
// use the "Family" variants as these will stay memoized for inserts, removals, and modifications.
//
// Note - like "set" this won't propogate instantly. Once all "gets" are dropped, only then will we run the
pub fn modify(&self) {}
}
pub fn use_recoil_callback<G>(f: impl Fn() -> G) -> RecoilContext<G> {
todo!()
}
}
+2 -1
View File
@@ -93,8 +93,9 @@ mod selectors {
}
impl<O> SelectorBuilder<O, false> {
pub fn getter(self, f: impl Fn(()) -> O) -> SelectorBuilder<O, true> {
std::rc::Rc::pin(value)
todo!()
// std::rc::Rc::pin(value)
// todo!()
}
}
pub struct selector<O>(pub fn(SelectorBuilder<O, false>) -> SelectorBuilder<O, true>);
+21 -29
View File
@@ -1,13 +1,9 @@
use crate::recoil::*;
use dioxus_core::prelude::Context;
pub static TODOS: AtomFamily<uuid::Uuid, TodoItem> = atom_family(|_| {});
pub static FILTER: Atom<FilterState> = atom(|_| FilterState::All);
pub static SHOW_ALL_TODOS: selector<bool> = selector(|g| g.getter(|f| false));
// an atomfamily is just a HashMap<K, Pin<Rc<V>>> that pins the Rc and exposes the values by reference
// we could do a more advanced management, but this is fine too
#[derive(PartialEq)]
pub enum FilterState {
All,
@@ -22,30 +18,26 @@ pub struct TodoItem {
pub contents: String,
}
pub fn add_todo(ctx: &Context, contents: String) {}
impl RecoilContext<()> {
pub fn add_todo(&self, contents: String) {}
pub fn remove_todo(ctx: &Context, id: &uuid::Uuid) {
TODOS.with(&ctx).remove(id)
pub fn remove_todo(&self, id: &uuid::Uuid) {
// TODOS.with().remove(id)
}
pub fn select_all_todos(&self) {}
pub fn toggle_todo(&self, id: &uuid::Uuid) {}
pub fn clear_completed(&self) {
// let (set, get) = (self.set, self.get);
// TOODS
// .get(&ctx)
// .iter()
// .filter(|(k, v)| v.checked)
// .map(|(k, v)| TODOS.remove(&ctx, k));
}
pub fn set_filter(&self, filter: &FilterState) {}
}
pub fn select_all_todos(ctx: &Context) {}
pub fn toggle_todo(ctx: &Context, id: &uuid::Uuid) {}
pub fn clear_completed(ctx: &Context) {
let (set, get) = (self.set, self.get);
TOODS
.get(&ctx)
.iter()
.filter(|(k, v)| v.checked)
.map(|(k, v)| TODOS.remove(&ctx, k));
}
pub fn set_filter(ctx: &Context, filter: &FilterState) {}
struct TodoManager<'a> {}
fn use_todos(ctx: &Context) {}
#[test]
fn test() {}
+21 -19
View File
@@ -1,7 +1,9 @@
use super::state::{FilterState, TodoItem, FILTER, TODOS};
use crate::filtertoggles;
use crate::recoil::use_atom;
use crate::todoitem::TodoEntry;
use crate::{
filtertoggles,
recoil::use_atom,
state::{FilterState, TodoItem, FILTER, TODOS},
todoitem::TodoEntry,
};
use dioxus_core::prelude::*;
pub fn TodoList(ctx: Context, props: &()) -> DomTree {
@@ -9,20 +11,6 @@ pub fn TodoList(ctx: Context, props: &()) -> DomTree {
let (todos, _) = use_state(&ctx, || Vec::<TodoItem>::new());
let filter = use_atom(&ctx, &FILTER);
let list = todos
.iter()
.filter(|item| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|item| {
rsx!(TodoEntry {
key: "{order}",
id: item.id,
})
});
ctx.render(rsx! {
div {
header {
@@ -36,7 +24,21 @@ pub fn TodoList(ctx: Context, props: &()) -> DomTree {
}
}
{list}
{ // list
todos
.iter()
.filter(|item| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|item| {
rsx!(TodoEntry {
key: "{order}",
id: item.id,
})
})
}
// filter toggle (show only if the list isn't empty)
{(!todos.is_empty()).then(||
+159
View File
@@ -0,0 +1,159 @@
use std::{collections::HashMap, rc::Rc};
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
#[derive(PartialEq)]
pub enum FilterState {
All,
Active,
Completed,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: uuid::Uuid,
pub checked: bool,
pub contents: String,
}
// =======================
// Components
// =======================
pub fn App(ctx: Context, props: &()) -> DomTree {
ctx.render(rsx! {
div {
id: "app"
style { "{APP_STYLE}" }
// list
TodoList {}
// footer
footer {
class: "info"
p {"Double-click to edit a todo"}
p {
"Created by "
a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }
}
p {
"Part of "
a { "TodoMVC", href: "http://todomvc.com" }
}
}
}
})
}
pub fn TodoList(ctx: Context, props: &()) -> DomTree {
let (draft, set_draft) = use_state(&ctx, || "".to_string());
let (todos, set_todos) = use_state(&ctx, || HashMap::<uuid::Uuid, Rc<TodoItem>>::new());
let (filter, set_filter) = use_state(&ctx, || FilterState::All);
ctx.render(rsx! {
div {
header {
class: "header"
h1 {"todos"}
input {
class: "new-todo"
placeholder: "What needs to be done?"
value: "{draft}"
oninput: move |evt| set_draft(evt.value)
}
}
{ // list
todos
.iter()
.filter(|(id, item)| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|(id, item)| {
rsx!(TodoEntry {
key: "{order}",
item: item.clone()
})
})
}
// filter toggle (show only if the list isn't empty)
{(!todos.is_empty()).then(||
rsx!( FilterToggles {})
)}
}
})
}
#[derive(PartialEq, Props)]
pub struct TodoEntryProps {
item: Rc<TodoItem>,
}
pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> DomTree {
let (is_editing, set_is_editing) = use_state(&ctx, || false);
let todo = &props.item;
ctx.render(rsx! (
li {
"{todo.id}"
input {
class: "toggle"
type: "checkbox"
"{todo.checked}"
}
{is_editing.then(|| rsx!(
input {
value: "{contents}"
}
))}
}
))
}
pub fn FilterToggles(ctx: Context, props: &()) -> DomTree {
let toggles = [
("All", "", FilterState::All),
("Active", "active", FilterState::Active),
("Completed", "completed", FilterState::Completed),
]
.iter()
.map(|(name, path, filter)| {
rsx!(
li {
class: "{name}"
a {
href: "{path}"
// onclick: move |_| reducer.set_filter(&filter)
"{name}"
}
}
)
});
// todo
let item_text = "";
let items_left = "";
ctx.render(rsx! {
footer {
span {
strong {"{items_left}"}
span {"{item_text} left"}
}
ul {
class: "filters"
{toggles}
}
}
})
}
+281
View File
@@ -0,0 +1,281 @@
use dioxus_core::prelude::*;
use dioxus_web::WebsysRenderer;
static APP_STYLE: &'static str = include_str!("./todomvc/style.css");
fn main() {
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
}
// =======================
// state-related items
// =======================
pub static TODOS: AtomFamily<uuid::Uuid, TodoItem> = atom_family(|_| {});
pub static FILTER: Atom<FilterState> = atom(|_| FilterState::All);
pub static SHOW_ALL_TODOS: selector<bool> = selector(|g| g.getter(|f| false));
#[derive(PartialEq)]
pub enum FilterState {
All,
Active,
Completed,
}
#[derive(Debug, PartialEq, Clone)]
pub struct TodoItem {
pub id: uuid::Uuid,
pub checked: bool,
pub contents: String,
}
impl RecoilContext<()> {
pub fn add_todo(&self, contents: String) {}
pub fn remove_todo(&self, id: &uuid::Uuid) {}
pub fn select_all_todos(&self) {}
pub fn toggle_todo(&self, id: &uuid::Uuid) {}
pub fn clear_completed(&self) {}
pub fn set_filter(&self, filter: &FilterState) {}
}
// =======================
// Components
// =======================
pub fn App(ctx: Context, props: &()) -> DomTree {
ctx.render(rsx! {
div {
id: "app"
style { "{APP_STYLE}" }
// list
TodoList {}
// footer
footer {
class: "info"
p {"Double-click to edit a todo"}
p {
"Created by "
a { "jkelleyrtp", href: "http://github.com/jkelleyrtp/" }
}
p {
"Part of "
a { "TodoMVC", href: "http://todomvc.com" }
}
}
}
})
}
pub fn TodoList(ctx: Context, props: &()) -> DomTree {
let (draft, set_draft) = use_state(&ctx, || "".to_string());
let (todos, _) = use_state(&ctx, || Vec::<TodoItem>::new());
let filter = use_atom(&ctx, &FILTER);
ctx.render(rsx! {
div {
header {
class: "header"
h1 {"todos"}
input {
class: "new-todo"
placeholder: "What needs to be done?"
value: "{draft}"
oninput: move |evt| set_draft(evt.value)
}
}
{ // list
todos
.iter()
.filter(|item| match filter {
FilterState::All => true,
FilterState::Active => !item.checked,
FilterState::Completed => item.checked,
})
.map(|item| {
rsx!(TodoEntry {
key: "{order}",
id: item.id,
})
})
}
// filter toggle (show only if the list isn't empty)
{(!todos.is_empty()).then(||
rsx!( FilterToggles {})
)}
}
})
}
#[derive(PartialEq, Props)]
pub struct TodoEntryProps {
id: uuid::Uuid,
}
pub fn TodoEntry(ctx: Context, props: &TodoEntryProps) -> DomTree {
let (is_editing, set_is_editing) = use_state(&ctx, || false);
let todo = use_atom_family(&ctx, &TODOS, props.id);
ctx.render(rsx! (
li {
"{todo.id}"
input {
class: "toggle"
type: "checkbox"
"{todo.checked}"
}
{is_editing.then(|| rsx!(
input {
value: "{contents}"
}
))}
}
))
}
pub fn FilterToggles(ctx: Context, props: &()) -> DomTree {
let reducer = recoil::use_callback(&ctx, || ());
let items_left = recoil::use_atom_family(&ctx, &TODOS, uuid::Uuid::new_v4());
let toggles = [
("All", "", FilterState::All),
("Active", "active", FilterState::Active),
("Completed", "completed", FilterState::Completed),
]
.iter()
.map(|(name, path, filter)| {
rsx!(
li {
class: "{name}"
a {
href: "{path}"
onclick: move |_| reducer.set_filter(&filter)
"{name}"
}
}
)
});
// todo
let item_text = "";
let items_left = "";
ctx.render(rsx! {
footer {
span {
strong {"{items_left}"}
span {"{item_text} left"}
}
ul {
class: "filters"
{toggles}
}
}
})
}
pub use recoil::*;
mod recoil {
use dioxus_core::context::Context;
pub struct RecoilContext<T: 'static> {
_inner: T,
}
impl<T: 'static> RecoilContext<T> {
/// Get the value of an atom. Returns a reference to the underlying data.
pub fn get(&self) {}
/// Replace an existing value with a new value
///
/// This does not replace the value instantly, and all calls to "get" within the current scope will return
pub fn set(&self) {}
// Modify lets you modify the value in place. However, because there's no previous value around to compare
// the new one with, we are unable to memoize the change. As such, all downsteam users of this Atom will
// be updated, causing all subsrcibed components to re-render.
//
// This is fine for most values, but might not be performant when dealing with collections. For collections,
// use the "Family" variants as these will stay memoized for inserts, removals, and modifications.
//
// Note - like "set" this won't propogate instantly. Once all "gets" are dropped, only then will we run the
pub fn modify(&self) {}
}
pub fn use_callback<'a, G>(c: &Context<'a>, f: impl Fn() -> G) -> &'a RecoilContext<G> {
todo!()
}
pub fn use_atom<T: PartialEq, O>(c: &Context, t: &'static Atom<T>) -> O {
todo!()
}
pub fn use_batom<T: PartialEq, O>(c: &Context, t: impl Readable) -> O {
todo!()
}
pub trait Readable {}
impl<T: PartialEq> Readable for &'static Atom<T> {}
impl<K: PartialEq, V: PartialEq> Readable for &'static AtomFamily<K, V> {}
pub fn use_atom_family<'a, K: PartialEq, V: PartialEq>(
c: &Context<'a>,
t: &'static AtomFamily<K, V>,
g: K,
) -> &'a V {
todo!()
}
pub use atoms::{atom, Atom};
pub use atoms::{atom_family, AtomFamily};
mod atoms {
use super::*;
pub struct AtomBuilder<T: PartialEq> {
pub key: String,
pub manual_init: Option<Box<dyn Fn() -> T>>,
_never: std::marker::PhantomData<T>,
}
impl<T: PartialEq> AtomBuilder<T> {
pub fn new() -> Self {
Self {
key: uuid::Uuid::new_v4().to_string(),
manual_init: None,
_never: std::marker::PhantomData {},
}
}
pub fn init<A: Fn() -> T + 'static>(&mut self, f: A) {
self.manual_init = Some(Box::new(f));
}
pub fn set_key(&mut self, _key: &'static str) {}
}
pub struct atom<T: PartialEq>(pub fn(&mut AtomBuilder<T>) -> T);
pub type Atom<T: PartialEq> = atom<T>;
pub struct AtomFamilyBuilder<K, V> {
_never: std::marker::PhantomData<(K, V)>,
}
pub struct atom_family<K: PartialEq, V: PartialEq>(pub fn(&mut AtomFamilyBuilder<K, V>));
pub type AtomFamily<K: PartialEq, V: PartialEq> = atom_family<K, V>;
}
pub use selectors::selector;
mod selectors {
pub struct SelectorBuilder<Out, const Built: bool> {
_p: std::marker::PhantomData<Out>,
}
impl<O> SelectorBuilder<O, false> {
pub fn getter(self, f: impl Fn(()) -> O) -> SelectorBuilder<O, true> {
todo!()
// std::rc::Rc::pin(value)
// todo!()
}
}
pub struct selector<O>(pub fn(SelectorBuilder<O, false>) -> SelectorBuilder<O, true>);
}
}