use core::fmt;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Executor;
impl Executor {
#[must_use]
pub const fn new() -> Self {
Self
}
pub fn execute<T, E, F>(&self, task: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
{
task()
}
}
impl fmt::Display for Executor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("synchronous executor")
}
}
#[cfg(test)]
mod tests {
use super::Executor;
use core::cell::Cell;
#[test]
fn executes_task_and_returns_value() {
let executor = Executor::new();
assert_eq!(executor.execute(|| Ok::<_, &'static str>(42)), Ok(42));
}
#[test]
fn invokes_one_shot_task_once() {
let executor = Executor::new();
let calls = Cell::new(0);
let result = executor.execute(|| {
calls.set(calls.get() + 1);
Ok::<_, &'static str>(())
});
assert_eq!(result, Ok(()));
assert_eq!(calls.get(), 1);
}
#[test]
fn preserves_task_error() {
let executor = Executor::new();
let result = executor.execute(|| Err::<(), _>("task-specific failure"));
assert_eq!(result, Err("task-specific failure"));
}
#[test]
fn invokes_task_once_when_it_returns_an_error() {
let executor = Executor::new();
let calls = Cell::new(0);
let result = executor.execute(|| {
calls.set(calls.get() + 1);
Err::<(), _>("task-specific failure")
});
assert_eq!(result, Err("task-specific failure"));
assert_eq!(calls.get(), 1);
}
#[test]
fn does_not_swallow_task_panics() {
let executor = Executor::new();
let result = std::panic::catch_unwind(|| {
executor.execute(|| -> Result<(), &'static str> {
panic!("task panic should propagate");
})
});
assert!(result.is_err());
}
#[test]
fn display_describes_execution_mode() {
assert_eq!(Executor::new().to_string(), "synchronous executor");
}
}