use std::fmt;
use std::io;
use windows_sys::Win32::Foundation::{
IORING_E_COMPLETION_QUEUE_TOO_BIG, IORING_E_COMPLETION_QUEUE_TOO_FULL, IORING_E_CORRUPT,
IORING_E_REQUIRED_FLAG_NOT_SUPPORTED, IORING_E_SUBMISSION_QUEUE_FULL,
IORING_E_SUBMISSION_QUEUE_TOO_BIG, IORING_E_SUBMIT_IN_PROGRESS, IORING_E_VERSION_NOT_SUPPORTED,
};
use windows_sys::core::HRESULT;
mod sealed {
pub trait Sealed {}
impl Sealed for std::io::Error {}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RingCondition {
RequiredFlagNotSupported,
VersionNotSupported,
SubmissionQueueFull,
SubmissionQueueTooBig,
CompletionQueueTooBig,
Corrupt,
SubmitInProgress,
CompletionQueueTooFull,
}
impl RingCondition {
#[must_use]
pub fn from_hresult(code: HRESULT) -> Option<Self> {
match code {
IORING_E_REQUIRED_FLAG_NOT_SUPPORTED => Some(Self::RequiredFlagNotSupported),
IORING_E_VERSION_NOT_SUPPORTED => Some(Self::VersionNotSupported),
IORING_E_SUBMISSION_QUEUE_FULL => Some(Self::SubmissionQueueFull),
IORING_E_SUBMISSION_QUEUE_TOO_BIG => Some(Self::SubmissionQueueTooBig),
IORING_E_COMPLETION_QUEUE_TOO_BIG => Some(Self::CompletionQueueTooBig),
IORING_E_CORRUPT => Some(Self::Corrupt),
IORING_E_SUBMIT_IN_PROGRESS => Some(Self::SubmitInProgress),
IORING_E_COMPLETION_QUEUE_TOO_FULL => Some(Self::CompletionQueueTooFull),
_ => None,
}
}
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::RequiredFlagNotSupported => "IORING_E_REQUIRED_FLAG_NOT_SUPPORTED",
Self::VersionNotSupported => "IORING_E_VERSION_NOT_SUPPORTED",
Self::SubmissionQueueFull => "IORING_E_SUBMISSION_QUEUE_FULL",
Self::SubmissionQueueTooBig => "IORING_E_SUBMISSION_QUEUE_TOO_BIG",
Self::CompletionQueueTooBig => "IORING_E_COMPLETION_QUEUE_TOO_BIG",
Self::Corrupt => "IORING_E_CORRUPT",
Self::SubmitInProgress => "IORING_E_SUBMIT_IN_PROGRESS",
Self::CompletionQueueTooFull => "IORING_E_COMPLETION_QUEUE_TOO_FULL",
}
}
#[must_use]
pub fn code(self) -> HRESULT {
match self {
Self::RequiredFlagNotSupported => IORING_E_REQUIRED_FLAG_NOT_SUPPORTED,
Self::VersionNotSupported => IORING_E_VERSION_NOT_SUPPORTED,
Self::SubmissionQueueFull => IORING_E_SUBMISSION_QUEUE_FULL,
Self::SubmissionQueueTooBig => IORING_E_SUBMISSION_QUEUE_TOO_BIG,
Self::CompletionQueueTooBig => IORING_E_COMPLETION_QUEUE_TOO_BIG,
Self::Corrupt => IORING_E_CORRUPT,
Self::SubmitInProgress => IORING_E_SUBMIT_IN_PROGRESS,
Self::CompletionQueueTooFull => IORING_E_COMPLETION_QUEUE_TOO_FULL,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct IoRingError {
code: HRESULT,
}
impl IoRingError {
pub(crate) fn new(code: HRESULT) -> Self {
Self { code }
}
#[must_use]
pub fn code(&self) -> HRESULT {
self.code
}
#[must_use]
pub fn name(&self) -> Option<&'static str> {
self.condition().map(RingCondition::name)
}
#[must_use]
pub fn condition(&self) -> Option<RingCondition> {
RingCondition::from_hresult(self.code)
}
#[must_use]
pub fn is_submission_queue_full(&self) -> bool {
self.condition() == Some(RingCondition::SubmissionQueueFull)
}
#[must_use]
pub fn is_completion_queue_too_full(&self) -> bool {
self.condition() == Some(RingCondition::CompletionQueueTooFull)
}
#[must_use]
pub fn is_submit_in_progress(&self) -> bool {
self.condition() == Some(RingCondition::SubmitInProgress)
}
}
pub trait IoRingErrorExt: sealed::Sealed {
fn as_ioring_error(&self) -> Option<&IoRingError>;
fn ring_condition(&self) -> Option<RingCondition>;
fn is_submission_queue_full(&self) -> bool;
fn is_completion_queue_too_full(&self) -> bool;
fn is_submit_in_progress(&self) -> bool;
}
impl IoRingErrorExt for io::Error {
fn as_ioring_error(&self) -> Option<&IoRingError> {
self.get_ref()?.downcast_ref::<IoRingError>()
}
fn ring_condition(&self) -> Option<RingCondition> {
self.as_ioring_error()?.condition()
}
fn is_submission_queue_full(&self) -> bool {
self.as_ioring_error()
.is_some_and(IoRingError::is_submission_queue_full)
}
fn is_completion_queue_too_full(&self) -> bool {
self.as_ioring_error()
.is_some_and(IoRingError::is_completion_queue_too_full)
}
fn is_submit_in_progress(&self) -> bool {
self.as_ioring_error()
.is_some_and(IoRingError::is_submit_in_progress)
}
}
impl fmt::Debug for IoRingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IoRingError")
.field("code", &format_args!("0x{:08X}", self.code as u32))
.finish()
}
}
impl fmt::Display for IoRingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.name() {
Some(name) => write!(f, "{name} (HRESULT 0x{:08X})", self.code as u32),
None => write!(f, "HRESULT 0x{:08X}", self.code as u32),
}
}
}
impl std::error::Error for IoRingError {}
#[non_exhaustive]
pub struct PushRefused<T, X> {
pub error: io::Error,
pub payload: Option<T>,
pub extra: X,
}
impl<T, X> PushRefused<T, X> {
pub(crate) fn new(error: io::Error, payload: Option<T>, extra: X) -> Self {
Self {
error,
payload,
extra,
}
}
#[must_use]
pub fn into_error(self) -> io::Error {
self.error
}
}
impl<T, X> From<PushRefused<T, X>> for io::Error {
fn from(refused: PushRefused<T, X>) -> Self {
refused.error
}
}
impl<T, X> fmt::Debug for PushRefused<T, X> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PushRefused")
.field("error", &self.error)
.field("payload", &self.payload.as_ref().map(|_| ".."))
.field("extra", &format_args!(".."))
.finish()
}
}
impl<T, X> fmt::Display for PushRefused<T, X> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "push refused: {}", self.error)
}
}
impl<T, X> std::error::Error for PushRefused<T, X> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
pub(crate) fn check(hr: HRESULT) -> io::Result<()> {
if hr < 0 {
Err(io::Error::other(IoRingError::new(hr)))
} else {
Ok(())
}
}
#[cfg(test)]
mod tests;