use libc::{__errno_location, c_int};
use std::ffi::{CStr, NulError};
use thiserror::Error;
use crate::process::ProcessError;
#[cfg(unix)]
#[derive(Error, Debug, PartialEq)]
pub enum RashError {
#[error("Null byte found in command at pos {}", pos)]
NullByteInCommand {
pos: usize,
},
#[error("{:?}", message)]
KernelError {
message: String,
},
#[error("Couldn't read stdout: {:?}", message)]
FailedToReadStdout {
message: String,
},
#[error("Couldn't read stderr: {:?}", message)]
FailedToReadStderr {
message: String,
},
}
impl From<ProcessError> for RashError {
fn from(v: ProcessError) -> Self {
fn into_kernel_error<S: AsRef<str>>(s: S) -> RashError {
RashError::KernelError {
message: unsafe { RashError::format_kernel_error_message(s) },
}
}
match v {
ProcessError::CouldNotFork
| ProcessError::CouldNotCreatePipe
| ProcessError::CouldNotDupFd(_)
| ProcessError::OpenDidNotCloseNormally => into_kernel_error(v.to_string()),
ProcessError::CouldNotGetStderr => RashError::FailedToReadStderr {
message: v.to_string(),
},
ProcessError::CouldNotGetStdout => RashError::FailedToReadStdout {
message: v.to_string(),
},
}
}
}
impl From<NulError> for RashError {
fn from(v: NulError) -> Self {
RashError::NullByteInCommand {
pos: v.nul_position(),
}
}
}
impl RashError {
pub(crate) unsafe fn format_kernel_error_message<S: AsRef<str>>(description: S) -> String {
let errno = *__errno_location();
let strerror = Self::strerror(errno);
format!(
"Received errno {}, Description: {}, strerror output: {strerror}.",
errno.to_string(),
description.as_ref()
)
}
unsafe fn strerror(errno: c_int) -> String {
let strerror = libc::strerror(errno);
if strerror.is_null() {
return "Couldn't get strerror - libc::strerror returned null.".to_string();
}
return match CStr::from_ptr(strerror).to_str() {
Ok(s) => s.to_string(),
Err(e) => e.to_string(),
};
}
}