use std::io;
use std::os::raw::c_int;
use std::str::Utf8Error;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
Os(io::Error),
Nvme(u32),
NotAvailable,
Utf8(Utf8Error),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Os(e) => write!(f, "libnvme: {e}"),
Error::Nvme(status) => write!(f, "libnvme: NVMe status 0x{status:04x}"),
Error::NotAvailable => write!(f, "libnvme: value not available"),
Error::Utf8(e) => write!(f, "libnvme: invalid UTF-8: {e}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Os(e) => Some(e),
Error::Utf8(e) => Some(e),
Error::Nvme(_) | Error::NotAvailable => None,
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::Os(e)
}
}
impl From<Utf8Error> for Error {
fn from(e: Utf8Error) -> Self {
Error::Utf8(e)
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub(crate) fn check_ret(ret: c_int) -> Result<()> {
if ret == 0 {
Ok(())
} else if ret < 0 {
let err = if ret == -1 {
io::Error::last_os_error()
} else {
io::Error::from_raw_os_error(-ret)
};
Err(Error::Os(err))
} else {
Err(Error::Nvme(ret as u32))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn enoent() -> Error {
io::Error::from_raw_os_error(2).into()
}
#[test]
fn display_has_libnvme_prefix() {
assert!(format!("{}", enoent()).starts_with("libnvme: "));
}
#[test]
fn source_is_inner_io_error() {
let err = enoent();
let source = std::error::Error::source(&err).expect("source should exist");
assert!(source.downcast_ref::<io::Error>().is_some());
}
#[test]
fn from_io_error_yields_os_variant() {
assert!(matches!(enoent(), Error::Os(_)));
}
#[test]
fn not_available_has_no_source() {
let err = Error::NotAvailable;
assert!(std::error::Error::source(&err).is_none());
}
#[test]
#[allow(invalid_from_utf8)]
fn utf8_error_displays_with_prefix() {
let bad = std::str::from_utf8(&[0xFFu8, 0xFE]).unwrap_err();
let err: Error = bad.into();
assert!(format!("{err}").starts_with("libnvme: invalid UTF-8"));
}
#[test]
fn nvme_status_formats_as_hex() {
let err = Error::Nvme(0x4081);
assert_eq!(format!("{err}"), "libnvme: NVMe status 0x4081");
}
#[test]
fn check_ret_zero_is_ok() {
assert!(check_ret(0).is_ok());
}
#[test]
fn check_ret_negative_is_os_error() {
let err = check_ret(-2).unwrap_err();
match err {
Error::Os(e) => assert_eq!(e.raw_os_error(), Some(2)),
_ => panic!("expected Os variant"),
}
}
#[test]
fn check_ret_positive_is_nvme_status() {
let err = check_ret(0x281).unwrap_err();
assert!(matches!(err, Error::Nvme(0x281)));
}
}