use std::os::raw::c_int;
use std::sync::{Arc, Mutex};
use std::{fmt, io};
pub(crate) type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone)]
#[non_exhaustive]
#[allow(variant_size_differences)]
pub enum ErrorKind {
TooManyVMPages,
#[non_exhaustive]
Io {
operation: &'static str,
error: Arc<io::Error>,
process_id: Option<libc::pid_t>,
},
#[non_exhaustive]
IntegerCast(std::num::TryFromIntError),
}
struct ErrorBackTrace {
backtrace: backtrace::Backtrace,
resolved: bool,
}
impl ErrorBackTrace {
fn resolve(&mut self) -> bool {
if !self.resolved {
self.resolved = true;
self.backtrace.resolve();
true
} else {
false
}
}
}
impl fmt::Debug for ErrorBackTrace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.backtrace, f)
}
}
#[derive(Clone)]
struct ErrorData {
kind: ErrorKind,
backtrace: Arc<Mutex<ErrorBackTrace>>,
}
impl ErrorData {
fn resolve_back_trace(&self) -> bool {
let mut back_trace_lock = self.backtrace.lock().unwrap();
back_trace_lock.resolve()
}
}
impl fmt::Debug for ErrorData {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
if self.resolve_back_trace() {
fmt::Debug::fmt(self, _f)
} else {
Ok(())
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Error(Box<ErrorData>);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0.kind {
ErrorKind::TooManyVMPages => {
write!(f, "virtual memory address range contains too many pages")
}
ErrorKind::Io {
operation,
error,
process_id,
} => match process_id {
None => write!(f, "{operation}: {error}"),
Some(process_id) => write!(f, "{operation}({process_id}): {error}"),
},
ErrorKind::IntegerCast(err) => err.fmt(f),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.0.kind {
ErrorKind::TooManyVMPages => None,
ErrorKind::Io { .. } => None,
ErrorKind::IntegerCast(err) => Some(err),
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
let backtrace = backtrace::Backtrace::new_unresolved();
Self(Box::new(ErrorData {
kind,
backtrace: Arc::new(Mutex::new(ErrorBackTrace {
backtrace,
resolved: false,
})),
}))
}
}
impl From<std::num::TryFromIntError> for Error {
fn from(err: std::num::TryFromIntError) -> Self {
Self::from(ErrorKind::IntegerCast(err))
}
}
impl Error {
pub(crate) fn from_io3(
error: io::Error,
operation: &'static str,
process_id: libc::pid_t,
) -> Self {
ErrorKind::Io {
operation,
error: Arc::new(error),
process_id: Some(process_id),
}
.into()
}
pub fn kind(&self) -> &ErrorKind {
&self.0.kind
}
pub fn os_error_code(&self) -> Option<c_int> {
match &self.0.kind {
ErrorKind::TooManyVMPages { .. } => None,
ErrorKind::Io { error, .. } => error.raw_os_error(),
ErrorKind::IntegerCast { .. } => None,
}
}
}