use core::cell::RefCell;
use core::future::poll_fn;
use core::task::{Context, Poll};
use embassy_sync::waitqueue::WakerRegistration;
pub enum State<CTX> {
Active,
Cancel(CTX),
Idle,
}
pub struct Inner<CTX> {
state: State<CTX>,
host: WakerRegistration,
controller: WakerRegistration,
}
pub struct CommandState<CTX> {
inner: RefCell<Inner<CTX>>,
}
impl<CTX: Clone + Copy> CommandState<CTX> {
pub fn new() -> Self {
Self {
inner: RefCell::new(Inner {
state: State::Idle,
host: WakerRegistration::new(),
controller: WakerRegistration::new(),
}),
}
}
fn with_inner<F: FnMut(&mut Inner<CTX>) -> R, R>(&self, mut f: F) -> R {
let mut inner = self.inner.borrow_mut();
f(&mut inner)
}
pub async fn request(&self) {
poll_fn(|cx| {
self.with_inner(|inner| {
inner.host.register(cx.waker());
match inner.state {
State::Idle => {
inner.state = State::Active;
Poll::Ready(())
}
_ => Poll::Pending,
}
})
})
.await
}
pub async fn wait_idle(&self) {
poll_fn(|cx| {
self.with_inner(|inner| {
inner.host.register(cx.waker());
match inner.state {
State::Idle => Poll::Ready(()),
_ => Poll::Pending,
}
})
})
.await
}
pub fn poll_cancelled(&self, cx: &mut Context<'_>) -> Poll<CTX> {
self.with_inner(|inner| {
inner.controller.register(cx.waker());
match inner.state {
State::Cancel(ctx) => Poll::Ready(ctx),
_ => Poll::Pending,
}
})
}
pub fn cancel(&self, ctx: CTX) {
self.with_inner(|inner| {
inner.state = State::Cancel(ctx);
inner.controller.wake();
})
}
pub fn canceled(&self) {
self.with_inner(|inner| {
inner.state = State::Idle;
inner.host.wake();
})
}
pub fn done(&self) {
self.with_inner(|inner| {
inner.state = State::Idle;
})
}
}