use quinn::{Connection, ConnectionError, StoppedError, WriteError};
use serde::Serialize;
use tokio::sync::oneshot;
pub struct SendingStateHandle {
result_receiver: oneshot::Receiver<SendingResult>,
}
impl SendingStateHandle {
pub(crate) fn from_result_receiver(
result_receiver: oneshot::Receiver<SendingResult>,
) -> SendingStateHandle {
SendingStateHandle { result_receiver }
}
pub fn get_state(mut self) -> SendingState {
match self.result_receiver.try_recv() {
Ok(result) => SendingState::Result(result),
Err(oneshot::error::TryRecvError::Closed) => SendingState::Result(SendingResult::Error),
Err(oneshot::error::TryRecvError::Empty) => SendingState::StillBeingSent(self),
}
}
}
pub enum SendingState {
StillBeingSent(SendingStateHandle),
Result(SendingResult),
}
pub enum SendingResult {
Sent,
WeClosed,
PeerClosedOrDied,
Error,
}
impl SendingResult {
pub fn sent(&self) -> bool {
matches!(self, SendingResult::Sent)
}
pub fn failed_due_to_connection_closed(&self) -> bool {
matches!(
self,
SendingResult::WeClosed | SendingResult::PeerClosedOrDied
)
}
pub fn failed_due_to_error(&self) -> bool {
matches!(self, SendingResult::Error)
}
pub(crate) fn from_result(result: Result<(), SendingError>) -> SendingResult {
match result {
Ok(()) => SendingResult::Sent,
Err(SendingError::OpenUni(connection_error))
| Err(SendingError::WriteAll(WriteError::ConnectionLost(connection_error)))
| Err(SendingError::Stopped(StoppedError::ConnectionLost(connection_error))) => {
SendingResult::from_connection_error(connection_error)
}
_ => {
SendingResult::Error
}
}
}
fn from_connection_error(connection_error: ConnectionError) -> SendingResult {
match connection_error {
ConnectionError::LocallyClosed => SendingResult::WeClosed,
ConnectionError::ApplicationClosed(_)
| ConnectionError::ConnectionClosed(_)
| ConnectionError::Reset
| ConnectionError::TimedOut => SendingResult::PeerClosedOrDied,
ConnectionError::VersionMismatch
| ConnectionError::TransportError(_)
| ConnectionError::CidsExhausted => SendingResult::Error,
}
}
}
#[derive(Debug)]
pub(crate) enum SendingError {
OpenUni(ConnectionError),
WriteAll(WriteError),
Stopped(StoppedError),
}
pub(crate) async fn send_message(
connection: &Connection,
message: &impl Serialize,
) -> Result<(), SendingError> {
let message_raw = rmp_serde::encode::to_vec(message).unwrap();
let mut stream = connection.open_uni().await.map_err(SendingError::OpenUni)?;
stream
.write_all(&message_raw)
.await
.map_err(SendingError::WriteAll)?;
stream.finish().unwrap();
stream.stopped().await.map_err(SendingError::Stopped)?;
Ok(())
}