use std::future::Future;
use std::io;
use std::pin::Pin;
use tokio::task::JoinHandle;
use super::Arrival;
use crate::supervision::executor;
use harmos_sidecar::attach::{End, Ending};
use harmos_sidecar::{Frame, transport};
struct DrivenEnd {
sender: transport::Sender,
task: Option<JoinHandle<io::Result<()>>>,
original: Option<Ending>,
failure: Option<String>,
}
pub(super) enum Connecting {
Unavailable,
Binary(transport::Sender, transport::Receiver),
}
pub(super) struct Sender(transport::Sender);
pub(super) struct Receiver(transport::Receiver);
impl Connecting {
pub fn new(data: transport::Io, control: Option<transport::Io>, ending: Option<Ending>) -> (Self, Option<Ending>) {
let Some(control) = control else {
return (Self::Unavailable, ending);
};
let transport::Connection { sender, receiver, mut driver } = transport::Connection::new(data, control);
let task = executor::task(async move { driver.run().await });
let ending = Ending::new(DrivenEnd { sender: sender.clone(), task: Some(task), original: ending, failure: None });
(Self::Binary(sender, receiver), Some(ending))
}
pub async fn connect(self) -> io::Result<(Sender, Receiver)> {
match self {
Self::Unavailable => {
Err(io::Error::new(io::ErrorKind::Unsupported, "attachment requires an independent binary control channel"))
}
Self::Binary(sender, receiver) => Ok((Sender(sender), Receiver(receiver))),
}
}
}
impl Sender {
pub async fn send(&self, frame: Frame) -> io::Result<()> {
self.0.send(frame).await
}
}
impl Receiver {
pub async fn receive(&mut self) -> io::Result<Option<Arrival>> {
Ok(self.0.receive().await?.map(Arrival::Binary))
}
}
impl End for DrivenEnd {
fn settle(&mut self) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + '_>> {
Box::pin(async move {
self.sender.close();
if let Some(task) = self.task.as_mut() {
let joined = task.await;
self.task.take();
if let Err(error) = joined {
self.failure = Some(error.to_string());
}
}
if let Some(original) = self.original.as_mut() {
original.settle().await?;
}
if let Some(failure) = &self.failure {
return Err(io::Error::other(failure.clone()));
}
self.original.take();
Ok(())
})
}
}
impl Drop for DrivenEnd {
fn drop(&mut self) {
self.sender.close();
}
}