Pure lasagna:
trait AppLogic {
fn run(&mut self, actions: Vec<(Id, Box<dyn Any>)>) -> Mutation;
}
A callback based approach to the actions:
struct Callbacks<T> {
callbacks: HashMap<Id, Box<dyn FnMut(Box<dyn Any>, &mut T)>>;
}
impl Callbacks {
fn add(&mut self, id: Id, f: FnMut(Box<dyn Any>, &mut T)) {...}
fn delete(&mut self, id) {...}
/// Maybe single action
fn run(&mut self, actions: Vec<(Id, Box<dyn Any>)>, data: &mut T) {...}
}
trait Component {
fn run(&mut self, callbacks: &mut Callbacks<Self>) -> Mutation
}
struct App<T: Component> {
callbacks: Callbacks<T>,
root: T,
}
impl AppLogic for App {
fn run(&mut self, actions: Vec<(Id, Box<dyn Any>)>) -> Mutation {
self.callbacks.run(actions, &mut self.root);
root.run(&mut self.callbacks);
}
}
Slight tweak to this: Component::run takes a callback context with add
and delete
methods (and has a &mut reference to the actual callback hashmap). To run a subcomponent, parent component lenses this context.
Vdom<T>
: nodes contain callbacks. Reconciliation updates the callbacks context, returns Mutation
.
trait VdomComponent {
fn run(&mut self) -> Vdom<Self>;
}
To wire in subcomponents. Add callbacks context to this run
method, not for self use, only by use of subcomponents. Call Component::run
, get Mutation
back. One node type of Vdom
is this mutation.
Other ideas: callback returns a local action type, unit at root. Lensing of subcomponent callbacks can consume this.
Elm-style events. Component has Event
associated type, event(&mut self, event: Self::Event)
method. Hashtable is not callbacks that mutate the component, but rather "half-lenses" that take Box<dyn Any>
action to component's event type.