use tokio::sync::oneshot;
use super::PendingAnswer;
use crate::prompt::AnswerPromptError;
#[derive(Debug)]
pub struct Reply<T> {
sender: oneshot::Sender<Result<T, AnswerPromptError>>,
}
impl<T> Reply<T> {
pub(super) fn channel() -> (Self, PendingAnswer<T>) {
let (sender, receiver) = oneshot::channel();
(Self { sender }, PendingAnswer::new(receiver))
}
pub async fn abandoned(&mut self) {
self.sender.closed().await;
}
pub fn answer(self, answer: T) {
drop(self.sender.send(Ok(answer)));
}
pub fn fail(self, error: AnswerPromptError) {
drop(self.sender.send(Err(error)));
}
pub fn is_abandoned(&self) -> bool {
self.sender.is_closed()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::missing_panics_doc)]
use super::*;
use crate::prompt::Confirmation;
#[tokio::test]
async fn abandoned_with_a_dropped_pending_answer_returns() {
let (mut reply, pending) = Reply::<Confirmation>::channel();
drop(pending);
reply.abandoned().await;
assert!(reply.is_abandoned());
}
#[tokio::test]
async fn fail_reports_the_error_to_the_command() {
let (reply, pending) = Reply::<Confirmation>::channel();
reply.fail(AnswerPromptError::ClosedInput);
assert!(match pending.wait().await.expect_err("should fail") {
AnswerPromptError::ClosedInput => true,
AnswerPromptError::DroppedRequest => false,
AnswerPromptError::UnscriptedPrompt => false,
AnswerPromptError::UnusableTerminal { .. } => false,
});
}
#[test]
fn is_abandoned_with_a_waiting_command_returns_false() {
let (reply, _pending) = Reply::<Confirmation>::channel();
let abandoned = reply.is_abandoned();
assert!(!abandoned);
}
#[test]
fn trait_send() {
fn assert_send<T: Send>() {}
assert_send::<Reply<Confirmation>>();
}
#[test]
fn trait_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<Reply<Confirmation>>();
}
#[test]
fn trait_unpin() {
fn assert_unpin<T: Unpin>() {}
assert_unpin::<Reply<Confirmation>>();
}
}