use std::future::Future;
use std::pin::Pin;
pub enum Action<Msg> {
None,
Task(Pin<Box<dyn Future<Output = Msg> + Send + 'static>>),
Batch(Vec<Action<Msg>>),
Quit,
}
impl<Msg> Action<Msg> {
pub fn task<F>(future: F) -> Self
where
F: Future<Output = Msg> + Send + 'static,
{
Action::Task(Box::pin(future))
}
pub fn map<N, F>(self, f: F) -> Action<N>
where
Msg: Send + 'static,
N: Send + 'static,
F: FnOnce(Msg) -> N + Send + 'static + Clone,
{
match self {
Action::None => Action::None,
Action::Quit => Action::Quit,
Action::Task(fut) => Action::Task(Box::pin(async move { f(fut.await) })),
Action::Batch(actions) => {
Action::Batch(actions.into_iter().map(|a| a.map(f.clone())).collect())
}
}
}
}
impl<Msg> std::fmt::Debug for Action<Msg> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Action::None => write!(f, "Action::None"),
Action::Task(_) => write!(f, "Action::Task(...)"),
Action::Batch(v) => write!(f, "Action::Batch(len={})", v.len()),
Action::Quit => write!(f, "Action::Quit"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn task_constructor() {
let action = Action::task(async { 42 });
assert!(matches!(action, Action::Task(_)));
}
#[test]
fn map_none() {
let action: Action<i32> = Action::None;
let mapped: Action<String> = action.map(|n| n.to_string());
assert!(matches!(mapped, Action::None));
}
#[test]
fn map_quit() {
let action: Action<i32> = Action::Quit;
let mapped: Action<String> = action.map(|n| n.to_string());
assert!(matches!(mapped, Action::Quit));
}
#[test]
fn map_batch() {
let action: Action<i32> = Action::Batch(vec![Action::None, Action::Quit]);
let mapped: Action<String> = action.map(|n| n.to_string());
match mapped {
Action::Batch(v) => {
assert_eq!(v.len(), 2);
assert!(matches!(v[0], Action::None));
assert!(matches!(v[1], Action::Quit));
}
_ => panic!("expected Batch"),
}
}
#[test]
fn debug_formatting() {
let none: Action<i32> = Action::None;
assert_eq!(format!("{none:?}"), "Action::None");
let quit: Action<i32> = Action::Quit;
assert_eq!(format!("{quit:?}"), "Action::Quit");
let task = Action::task(async { 1 });
assert_eq!(format!("{task:?}"), "Action::Task(...)");
let batch: Action<i32> = Action::Batch(vec![Action::None]);
assert_eq!(format!("{batch:?}"), "Action::Batch(len=1)");
}
}