libdw 0.0.6

Bindings to elfutils
Documentation
use crate::ffi;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::io;
use std::result;

macro_rules! raw_ffi {
    ($func:ident ($($arg:expr),*)) => ({
        #[allow(unused_unsafe)]
        unsafe { ffi::$func($($arg),*) }
    })
}

macro_rules! ffi {
    ($func:ident ($($arg:expr),*)) => ({
        let result = raw_ffi!($func($($arg),*));
        crate::error::IntoResult::into_result(result)
    })
}

macro_rules! ffi_check {
    ($func:ident ($($arg:expr),*) != $error:expr) => ({
        let result = raw_ffi!($func($($arg),*));
        if result != $error {
            Ok(result)
        } else {
            Err(crate::Error::last())
        }
    })
}

pub type Result<T> = result::Result<T, Error>;

pub(crate) trait IntoResult: Sized {
    fn into_result(self) -> Result<Self>;
}

impl IntoResult for libc::c_int {
    #[inline]
    fn into_result(self) -> Result<Self> {
        if self < 0 {
            Err(Error::last())
        } else {
            Ok(self)
        }
    }
}

impl IntoResult for isize {
    #[inline]
    fn into_result(self) -> Result<Self> {
        if self < 0 {
            Err(Error::last())
        } else {
            Ok(self)
        }
    }
}

impl<T> IntoResult for *const T {
    #[inline]
    fn into_result(self) -> Result<Self> {
        if self.is_null() {
            Err(Error::last())
        } else {
            Ok(self)
        }
    }
}

impl<T> IntoResult for *mut T {
    #[inline]
    fn into_result(self) -> Result<Self> {
        if self.is_null() {
            Err(Error::last())
        } else {
            Ok(self)
        }
    }
}

#[derive(Debug)]
pub struct Error {
    kind: ErrorKind,
}

#[derive(Debug)]
enum ErrorKind {
    Dw(libc::c_int),
    Io(io::Error),
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Error {
        Error {
            kind: ErrorKind::Io(error),
        }
    }
}

impl Error {
    #[inline]
    pub fn last() -> Error {
        let errno = raw_ffi!(dwarf_errno());
        Error {
            kind: ErrorKind::Dw(errno),
        }
    }

    #[inline]
    pub fn check() -> Option<Error> {
        let error = Error::last();
        if let ErrorKind::Dw(0) = error.kind {
            None
        } else {
            Some(error)
        }
    }
}

#[inline]
fn errmsg(errno: libc::c_int) -> &'static CStr {
    // Normalize 0 to -1, which behaves the same except it always returns a legal string
    let errno = match errno {
        0 => -1,
        e => e,
    };
    let msg = raw_ffi!(dwarf_errmsg(errno));
    unsafe { CStr::from_ptr(msg) }
}

impl error::Error for Error {
    #[inline]
    fn description(&self) -> &str {
        match self.kind {
            ErrorKind::Dw(errno) => errmsg(errno).to_str().unwrap_or("libdw error"),
            ErrorKind::Io(ref error) => error.description(),
        }
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.kind {
            ErrorKind::Dw(errno) => {
                let msg = errmsg(errno);
                match msg.to_str() {
                    Ok(s) => fmt::Display::fmt(s, f),
                    Err(_) => fmt::Debug::fmt(msg, f),
                }
            }
            ErrorKind::Io(ref error) => fmt::Display::fmt(&error, f),
        }
    }
}