use core::fmt;
#[cfg(feature = "demo-app")]
use std::io;
#[cfg(feature = "session")]
use crate::congestion::{count_tp, time_tp};
use crate::protocol::pkt_format::PacketError;
#[derive(Debug)]
pub enum UdpSocketError {
NotSupported(&'static str),
Syscall {
call: &'static str,
code: i32,
},
InvalidInput(&'static str),
UnsupportedAddress,
UnexpectedControlMessage { level: i32, ty: i32 },
}
impl fmt::Display for UdpSocketError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UdpSocketError::NotSupported(what) => write!(f, "not supported: {what}"),
UdpSocketError::Syscall { call, code } => {
write!(f, "{call} failed (os error {code})")
}
UdpSocketError::InvalidInput(msg) => write!(f, "invalid input: {msg}"),
UdpSocketError::UnsupportedAddress => write!(f, "unsupported address type"),
UdpSocketError::UnexpectedControlMessage { level, ty } => {
write!(f, "unexpected control message (level={level}, type={ty})")
}
}
}
}
impl std::error::Error for UdpSocketError {}
#[cfg(feature = "demo-app")]
#[derive(Debug)]
pub enum AppError {
MissingValue(&'static str),
InvalidValue(&'static str),
Usage(String),
JsonWriter(io::Error),
Exit(String),
}
#[cfg(feature = "demo-app")]
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::MissingValue(flag) => write!(f, "{flag} requires value"),
AppError::InvalidValue(detail) => write!(f, "Error during converting {detail}"),
AppError::Usage(msg) | AppError::Exit(msg) => f.write_str(msg),
AppError::JsonWriter(err) => err.fmt(f),
}
}
}
#[cfg(feature = "demo-app")]
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::JsonWriter(err) => Some(err),
_ => None,
}
}
}
#[cfg(feature = "demo-app")]
impl From<io::Error> for AppError {
fn from(value: io::Error) -> Self {
AppError::JsonWriter(value)
}
}
#[derive(Debug)]
pub enum RunnerError {
Socket(UdpSocketError),
Packet(PacketError),
StartupTriggerTimeout {
waited_us: u32,
},
ConsecutiveTimeouts,
}
impl fmt::Display for RunnerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RunnerError::Socket(err) => err.fmt(f),
RunnerError::Packet(err) => err.fmt(f),
RunnerError::StartupTriggerTimeout { waited_us } => {
write!(
f,
"timed out after {waited_us} us while waiting for the startup trigger packet"
)
}
RunnerError::ConsecutiveTimeouts => {
f.write_str("stop prague sender due to consecutive timeout")
}
}
}
}
impl std::error::Error for RunnerError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
RunnerError::Socket(err) => Some(err),
RunnerError::Packet(err) => Some(err),
RunnerError::StartupTriggerTimeout { .. } => None,
RunnerError::ConsecutiveTimeouts => None,
}
}
}
impl From<UdpSocketError> for RunnerError {
fn from(value: UdpSocketError) -> Self {
RunnerError::Socket(value)
}
}
impl From<PacketError> for RunnerError {
fn from(value: PacketError) -> Self {
RunnerError::Packet(value)
}
}
#[cfg(feature = "session")]
#[derive(Debug)]
pub enum SessionError {
Socket(UdpSocketError),
Packet(PacketError),
PayloadTooLarge {
payload_len: usize,
max_payload_len: usize,
},
FrameAlreadyQueued,
FrameNotReady {
next_frame_in_us: time_tp,
},
FeedbackTimeout {
waited_us: time_tp,
inflight_packets: count_tp,
},
WouldBlock {
next_send_in_us: time_tp,
inflight_packets: count_tp,
packet_window: count_tp,
},
UnexpectedPacketType(u8),
InvalidPacket(&'static str),
}
#[cfg(feature = "session")]
impl fmt::Display for SessionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SessionError::Socket(err) => err.fmt(f),
SessionError::Packet(err) => err.fmt(f),
SessionError::PayloadTooLarge {
payload_len,
max_payload_len,
} => write!(
f,
"payload too large: requested {payload_len} bytes but only {max_payload_len} bytes fit"
),
SessionError::FrameAlreadyQueued => {
f.write_str("a video frame is already queued")
}
SessionError::FrameNotReady { next_frame_in_us } => {
write!(f, "next video frame slot opens in {next_frame_in_us} us")
}
SessionError::FeedbackTimeout {
waited_us,
inflight_packets,
} => write!(
f,
"timed out after {waited_us} us while waiting for feedback (inflight {inflight_packets})"
),
SessionError::WouldBlock {
next_send_in_us,
inflight_packets,
packet_window,
} => write!(
f,
"session would block (next send in {next_send_in_us} us, inflight {inflight_packets}/{packet_window})"
),
SessionError::UnexpectedPacketType(ty) => {
write!(f, "unexpected packet type 0x{ty:02x}")
}
SessionError::InvalidPacket(msg) => f.write_str(msg),
}
}
}
#[cfg(feature = "session")]
impl std::error::Error for SessionError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SessionError::Socket(err) => Some(err),
SessionError::Packet(err) => Some(err),
SessionError::PayloadTooLarge { .. }
| SessionError::FrameAlreadyQueued
| SessionError::FrameNotReady { .. }
| SessionError::FeedbackTimeout { .. }
| SessionError::WouldBlock { .. }
| SessionError::UnexpectedPacketType(_)
| SessionError::InvalidPacket(_) => None,
}
}
}
#[cfg(feature = "session")]
impl From<UdpSocketError> for SessionError {
fn from(value: UdpSocketError) -> Self {
SessionError::Socket(value)
}
}
#[cfg(feature = "session")]
impl From<PacketError> for SessionError {
fn from(value: PacketError) -> Self {
SessionError::Packet(value)
}
}