Skip to content

Commit

Permalink
refactor: use Action for wrapping Action and Sub
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanceras committed Apr 1, 2024
1 parent 103c07b commit 4585699
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 21 deletions.
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
- [X] Rename `RecurringTask` to `Sub`
- [ ] enum Task{Cmd,Sub} into one unified type.
- Sauron just consilidate them into one enum struct for simplicity
- [ ] Remove `Modifier` and `measurements`

## Features
- [X] Storage service (May not be needed since the user can directly use web-sys)
Expand Down
19 changes: 12 additions & 7 deletions crates/core/src/dom/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ where
}
}


impl<APP, IN> From<IN> for Dispatch<APP>
where
APP: Application,
Expand All @@ -153,13 +154,17 @@ impl<APP> From<Task<APP::MSG>> for Dispatch<APP>
where
APP: Application,
{
fn from(mut task: Task<APP::MSG>) -> Self {
Dispatch::new(move |mut program| {
spawn_local(async move {
while let Some(msg) = task.next().await {
program.dispatch(msg)
}
});
fn from(task: Task<APP::MSG>) -> Self {
Dispatch::new(move |program| {
for mut command in task.commands.into_iter(){
let program = program.downgrade();
spawn_local(async move {
let mut program = program.upgrade().expect("upgrade");
while let Some(msg) = command.next().await {
program.dispatch(msg)
}
});
}
})
}
}
95 changes: 81 additions & 14 deletions crates/core/src/dom/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,22 @@ use futures::channel::mpsc::UnboundedReceiver;
use futures::StreamExt;
use std::future::Future;
use std::pin::Pin;
use crate::dom::Effects;

/// encapsulate anything a component can do
pub enum Task<MSG> {
pub enum Command<MSG> {
/// A task with one single resulting MSG
Single(SingleTask<MSG>),
Action(Action<MSG>),
/// A task with recurring resulting MSG
Sub(Sub<MSG>),
}

///
pub struct Task<MSG>{
/// commands
pub(crate) commands: Vec<Command<MSG>>,
}

impl<MSG> Task<MSG>
where
MSG: 'static,
Expand All @@ -23,7 +30,67 @@ where
where
F: Future<Output = MSG> + 'static,
{
Self::Single(SingleTask::new(f))
Self{
commands: vec![Command::single(f)]
}
}
///
pub fn sub(rx: UnboundedReceiver<MSG>, event_closure: EventClosure) -> Self {
Self{
commands: vec![Command::sub(rx, event_closure)],
}
}

/// map the msg of this Task such that Task<MSG> becomes Task<MSG2>.
pub fn map_msg<F, MSG2>(self, f: F) -> Task<MSG2>
where
F: Fn(MSG) -> MSG2 + 'static + Clone,
MSG2: 'static,
{
Task{
commands: self.commands.into_iter().map(|t|t.map_msg(f.clone())).collect(),
}
}

/// batch together multiple Task into one task
pub fn batch(tasks: impl IntoIterator<Item = Self>) -> Self {
let mut commands = vec![];
for task in tasks.into_iter(){
commands.extend(task.commands);
}
Self {commands}
}

}

impl<MSG> From<Effects<MSG, MSG>> for Task<MSG>
where MSG: 'static
{
/// Convert Effects that has only follow ups
fn from(effects: Effects<MSG, MSG>) -> Self {
// we can safely ignore the effects here
// as there is no content on it.
let Effects {
local,
external,
modifier:_,
} = effects;

Task::batch(local.into_iter().chain(external.into_iter()).map(Task::from))
}
}


impl<MSG> Command<MSG>
where
MSG: 'static,
{
///
pub fn single<F>(f: F) -> Self
where
F: Future<Output = MSG> + 'static,
{
Self::Action(Action::new(f))
}
///
pub fn sub(rx: UnboundedReceiver<MSG>, event_closure: EventClosure) -> Self {
Expand All @@ -34,36 +101,36 @@ where
}

/// apply a function to the msg to create a different task which has a different msg
pub fn map_msg<F, MSG2>(self, f: F) -> Task<MSG2>
pub fn map_msg<F, MSG2>(self, f: F) -> Command<MSG2>
where
F: Fn(MSG) -> MSG2 + 'static,
MSG2: 'static,
{
match self {
Self::Single(task) => Task::Single(task.map_msg(f)),
Self::Sub(task) => Task::Sub(task.map_msg(f)),
Self::Action(task) => Command::Action(task.map_msg(f)),
Self::Sub(task) => Command::Sub(task.map_msg(f)),
}
}

/// return the next value
pub async fn next(&mut self) -> Option<MSG> {
match self {
Self::Single(task) => task.next().await,
Self::Action(task) => task.next().await,
Self::Sub(task) => task.next().await,
}
}
}

/// SingleTask is used to do asynchronous operations
pub struct SingleTask<MSG> {
/// Action is used to do asynchronous operations
pub struct Action<MSG> {
task: Pin<Box<dyn Future<Output = MSG>>>,
/// a marker to indicate if the value of the future is awaited.
/// any attempt to await it again will error,
/// saying that the async function is resumed after completion.
done: bool,
}

impl<MSG> SingleTask<MSG>
impl<MSG> Action<MSG>
where
MSG: 'static,
{
Expand All @@ -80,13 +147,13 @@ where


/// apply a function to the msg to create a different task which has a different msg
fn map_msg<F, MSG2>(self, f: F) -> SingleTask<MSG2>
fn map_msg<F, MSG2>(self, f: F) -> Action<MSG2>
where
F: Fn(MSG) -> MSG2 + 'static,
MSG2: 'static,
{
let task = self.task;
SingleTask::new(async move {
Action::new(async move {
let msg = task.await;
f(msg)
})
Expand All @@ -106,13 +173,13 @@ where
}
}

impl<F, MSG> From<F> for SingleTask<MSG>
impl<F, MSG> From<F> for Action<MSG>
where
F: Future<Output = MSG> + 'static,
MSG: 'static,
{
fn from(f: F) -> Self {
SingleTask::new(f)
Action::new(f)
}
}

Expand Down

0 comments on commit 4585699

Please sign in to comment.