use std::time::Duration;
use crate::enquiry::dialogue::Dialogue;
use crate::enquiry::error::{AskError, DialogueError, DialogueTimeoutError, ListenError, ListenTimeoutError};
use tokio::sync::{mpsc, oneshot};
#[derive(Debug)]
pub struct Questioner<Q, A> {
question_sender: mpsc::Sender<Dialogue<Q,A>>,
}
#[derive(Debug)]
pub struct QuestionAsked<A> {
answer_receiver: oneshot::Receiver<A>
}
impl<Q, A> Questioner<Q,A> {
pub(super) fn new(question_sender: mpsc::Sender<Dialogue<Q, A>>) -> Self {
Self {
question_sender
}
}
pub async fn ask(&self, message: Q) -> Result<QuestionAsked<A>, AskError<Q>> {
let (answer_sender, answer_receiver) = tokio::sync::oneshot::channel();
let dialogue = Dialogue::new(message, answer_sender);
self.question_sender.send(dialogue).await
.map_err(|e| AskError::new(e.0.into_parts().0))?;
Ok(
QuestionAsked {
answer_receiver,
}
)
}
pub async fn ask_and_listen(&self, message: Q) -> Result<A, DialogueError<Q>> {
self.ask(message).await
.map_err(DialogueError::Ask)?
.listen().await
.map_err(|_| DialogueError::Listen)
}
pub async fn ask_and_listen_or_timeout(&self, message: Q, timeout: impl Into<Duration>) -> Result<A, DialogueTimeoutError<Q>> {
self.ask(message).await
.map_err(DialogueTimeoutError::Ask)?
.listen_or_timeout(timeout).await
.map_err(|e| e.into())
}
}
impl<A> QuestionAsked<A> {
pub async fn listen(self) -> Result<A, ListenError> {
self.answer_receiver.await
.map_err(|_| ListenError(()))
}
pub async fn listen_or_timeout(self, timeout: impl Into<Duration>) -> Result<A, ListenTimeoutError> {
tokio::select! {
biased;
result = self.answer_receiver => {
result.map_err(|_| ListenTimeoutError::Disconnected)
},
_ = tokio::time::sleep(timeout.into()) => {
Err(ListenTimeoutError::Timeout)
}
}
}
}
impl<Q, A> std::clone::Clone for Questioner<Q, A> {
fn clone(&self) -> Self {
Self {
question_sender: self.question_sender.clone(),
}
}
}