use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ErrorKind {
Io,
UnexpectedEof,
Protocol,
InvalidHeader,
Overflow,
Timeout,
WouldBlock,
H2(u32),
Grpc(u32),
Canceled,
Other,
}
#[derive(Debug, Clone)]
pub struct Error {
pub kind: ErrorKind,
pub message: Option<Box<str>>,
}
pub type Result<T, E = Error> = core::result::Result<T, E>;
impl Error {
#[inline]
pub fn new(kind: ErrorKind) -> Self {
Self {
kind,
message: None,
}
}
#[inline]
pub fn with_message(kind: ErrorKind, message: impl Into<String>) -> Self {
Self {
kind,
message: Some(message.into().into_boxed_str()),
}
}
#[inline]
pub fn io(msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Io, msg)
}
#[inline]
pub fn timeout(msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Timeout, msg)
}
#[inline]
pub fn eof() -> Self {
Self::new(ErrorKind::UnexpectedEof)
}
#[inline]
pub fn protocol(msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Protocol, msg)
}
#[inline]
pub fn overflow(msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Overflow, msg)
}
#[inline]
pub fn canceled(msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Canceled, msg)
}
#[inline]
pub fn h2(code: u32, msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::H2(code), msg)
}
#[inline]
pub fn grpc(code: u32, msg: impl Into<String>) -> Self {
Self::with_message(ErrorKind::Grpc(code), msg)
}
#[inline]
pub fn invalid_header_name() -> Self {
Self::with_message(ErrorKind::InvalidHeader, "invalid header name")
}
#[inline]
pub fn invalid_header_value() -> Self {
Self::with_message(ErrorKind::InvalidHeader, "invalid header value")
}
#[inline]
pub fn h2_code(&self) -> Option<u32> {
match self.kind {
ErrorKind::H2(c) => Some(c),
_ => None,
}
}
#[inline]
pub fn grpc_code(&self) -> Option<u32> {
match self.kind {
ErrorKind::Grpc(c) => Some(c),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ErrorKind::H2(c) => write!(f, "HTTP/2 error 0x{c:x}")?,
ErrorKind::Grpc(c) => write!(f, "gRPC status {c}")?,
k => write!(f, "{k:?}")?,
}
if let Some(m) = &self.message {
write!(f, ": {m}")?;
}
Ok(())
}
}
impl core::error::Error for Error {}
impl From<core::str::Utf8Error> for Error {
fn from(e: core::str::Utf8Error) -> Self {
Self::with_message(ErrorKind::InvalidHeader, e.to_string())
}
}
impl From<core::fmt::Error> for Error {
fn from(_: core::fmt::Error) -> Self {
Self::new(ErrorKind::Other)
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
use std::io::ErrorKind as K;
let kind = match e.kind() {
K::UnexpectedEof => ErrorKind::UnexpectedEof,
K::TimedOut => ErrorKind::Timeout,
K::ConnectionReset | K::ConnectionAborted | K::BrokenPipe => ErrorKind::Canceled,
_ => ErrorKind::Io,
};
Self::with_message(kind, e.to_string())
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(e: Error) -> Self {
std::io::Error::other(e)
}
}