use sqsh_sys as ffi;
use bstr::BStr;
use std::error::Error as StdError;
use std::ffi::{c_int, CStr};
use std::fmt::{Debug, Display, Formatter};
use std::io;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Error(pub ffi::SqshError);
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub(crate) const fn new(err: c_int) -> Error {
let err = err.unsigned_abs();
Error(ffi::SqshError(err))
}
impl Error {
unsafe fn with_str<F, O>(&self, f: F) -> O
where
F: FnOnce(&CStr) -> O,
{
let s = ffi::sqsh_error_str(self.0 .0 as c_int);
f(CStr::from_ptr(s))
}
#[must_use]
pub fn io_error_kind(&self) -> io::ErrorKind {
let Self(err) = *self;
if err.0 < ffi::SqshError::SQSH_ERROR_SECTION_START.0 {
let io_err = io::Error::from_raw_os_error(err.0 as _);
return io_err.kind();
}
match err {
ffi::SqshError::SQSH_ERROR_UNKNOWN_FILE_TYPE
| ffi::SqshError::SQSH_ERROR_COMPRESSION_UNSUPPORTED
| ffi::SqshError::SQSH_ERROR_CORRUPTED_DIRECTORY_ENTRY
| ffi::SqshError::SQSH_ERROR_CORRUPTED_DIRECTORY_HEADER
| ffi::SqshError::SQSH_ERROR_CORRUPTED_INODE
| ffi::SqshError::SQSH_ERROR_BLOCKSIZE_MISMATCH
| ffi::SqshError::SQSH_ERROR_SIZE_MISMATCH
| ffi::SqshError::SQSH_ERROR_XATTR_SIZE_MISMATCH
| ffi::SqshError::SQSH_ERROR_INODE_MAP_IS_INCONSISTENT
| ffi::SqshError::SQSH_ERROR_INODE_PARENT_MISMATCH
| ffi::SqshError::SQSH_ERROR_INODE_PARENT_UNSET
| ffi::SqshError::SQSH_ERROR_NOT_A_SYMLINK => io::ErrorKind::InvalidData,
ffi::SqshError::SQSH_ERROR_NO_SUCH_FILE | ffi::SqshError::SQSH_ERROR_NO_SUCH_XATTR => {
io::ErrorKind::NotFound
}
ffi::SqshError::SQSH_ERROR_INVALID_ARGUMENT => io::ErrorKind::InvalidInput,
_ => io::ErrorKind::Other,
}
}
#[must_use]
pub fn as_io_error(&self) -> Option<io::Error> {
let Self(err) = *self;
if err.0 < ffi::SqshError::SQSH_ERROR_SECTION_START.0 {
Some(io::Error::from_raw_os_error(err.0 as _))
} else {
None
}
}
#[must_use]
pub fn into_io_error(self) -> io::Error {
match self.as_io_error() {
Some(err) => err,
None => io::Error::new(self.io_error_kind(), self),
}
}
#[must_use]
pub(crate) fn to_ffi_result(self) -> c_int {
-(self.0 .0 as c_int)
}
}
impl From<std::ffi::NulError> for Error {
fn from(_: std::ffi::NulError) -> Self {
Self(ffi::SqshError::SQSH_ERROR_INVALID_ARGUMENT)
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> Self {
err.into_io_error()
}
}
impl From<ffi::SqshError> for Error {
fn from(err: ffi::SqshError) -> Self {
Self(err)
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(_: std::num::TryFromIntError) -> Self {
Self(ffi::SqshError::SQSH_ERROR_INTEGER_OVERFLOW)
}
}
impl Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe { self.with_str(|s| Debug::fmt(s, f)) }
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
unsafe { self.with_str(|s| Display::fmt(BStr::new(s.to_bytes()), f)) }
}
}
impl StdError for Error {}