#![expect(clippy::panic)]
use std::{future::poll_fn, sync::Arc, task::Poll};
use either::Either;
use parking_lot::Mutex;
use crate::{
BoxFuture, SendData,
effect::{StageEffect, StageResponse},
};
pub(crate) type EffectBox = Arc<Mutex<Option<Either<StageEffect<Box<dyn SendData>>, StageResponse>>>>;
pub(crate) fn airlock_effect<Out>(
eb: &EffectBox,
effect: StageEffect<Box<dyn SendData>>,
mut response: impl FnMut(Option<StageResponse>) -> Option<Out> + Send + 'static,
) -> BoxFuture<'static, Out> {
let eb = eb.clone();
let mut effect = Some(effect);
Box::pin(poll_fn(move |_| {
let mut eb = eb.lock();
if let Some(effect) = effect.take() {
match eb.take() {
Some(Either::Left(x)) => panic!("effect already set: {:?}", x),
Some(Either::Right(StageResponse::Unit)) | None => {}
Some(Either::Right(resp)) => {
panic!("effect airlock contains leftover response: {:?}", resp)
}
}
*eb = Some(Either::Left(effect));
Poll::Pending
} else {
let Some(out) = eb.take() else {
return Poll::Pending;
};
let out = match out {
Either::Left(x) => panic!("expected response, got effect: {:?}", x),
Either::Right(x) => response(Some(x)),
};
out.map(Poll::Ready).unwrap_or(Poll::Pending)
}
}))
}