use core::ffi::c_int;
use core::fmt;
use crate::SystemError;
use bun_core::String as BunString;
use crate::{E, Fd, SystemErrno, Tag, coreutils_error_map, libuv_error_map};
#[inline]
fn errno_to_err(errno: Int) -> bun_core::Error {
bun_core::Error::from_errno(errno as i32)
}
#[inline]
fn fd_unwrap_valid(fd: Fd) -> Option<Fd> {
if fd == Fd::INVALID { None } else { Some(fd) }
}
#[cfg(windows)]
const RETRY_ERRNO: Int = E::EINTR as Int;
#[cfg(not(windows))]
const RETRY_ERRNO: Int = E::EAGAIN as Int;
const TODO_ERRNO: Int = Int::MAX - 1;
pub(crate) type Int = u16;
#[derive(Clone, Debug)]
pub struct Error {
pub errno: Int,
pub fd: Fd,
#[cfg(windows)]
pub from_libuv: bool,
pub path: Box<[u8]>,
pub syscall: Tag,
pub dest: Box<[u8]>,
}
impl Default for Error {
fn default() -> Self {
Self {
errno: TODO_ERRNO,
fd: Fd::INVALID,
#[cfg(windows)]
from_libuv: false,
path: Box::default(),
syscall: Tag::TODO,
dest: Box::default(),
}
}
}
pub trait IntoErrnoInt {
fn into_errno_int(self) -> Int;
}
impl IntoErrnoInt for E {
#[inline]
fn into_errno_int(self) -> Int {
self as Int
}
}
#[cfg(windows)]
impl IntoErrnoInt for SystemErrno {
#[inline]
fn into_errno_int(self) -> Int {
self as Int
}
}
impl IntoErrnoInt for u16 {
#[inline]
fn into_errno_int(self) -> Int {
self
}
}
impl IntoErrnoInt for i32 {
#[inline]
fn into_errno_int(self) -> Int {
#[cfg(windows)]
{
self.unsigned_abs() as Int
}
#[cfg(not(windows))]
{
Int::try_from(self).expect("errno must be non-negative on POSIX")
}
}
}
impl Error {
#[inline]
pub fn new<C: IntoErrnoInt>(errno: C, syscall_tag: Tag) -> Error {
Error {
errno: errno.into_errno_int(),
syscall: syscall_tag,
..Default::default()
}
}
#[cfg(windows)]
#[inline]
pub fn from_uv_rc(rc: crate::windows::libuv::ReturnCode, syscall_tag: Tag) -> Option<Error> {
rc.errno().map(|e| Error {
errno: e,
syscall: syscall_tag,
..Default::default()
})
}
#[cfg(windows)]
#[inline]
pub fn from_uv_rc64(
rc: crate::windows::libuv::ReturnCodeI64,
syscall_tag: Tag,
) -> Option<Error> {
rc.errno().map(|e| Error {
errno: e,
syscall: syscall_tag,
..Default::default()
})
}
pub fn from_code(errno: E, syscall_tag: Tag) -> Error {
Error {
errno: errno as Int,
syscall: syscall_tag,
..Default::default()
}
}
pub fn from_code_int(errno: c_int, syscall_tag: Tag) -> Error {
#[cfg(windows)]
let n = Int::try_from(errno.unsigned_abs()).unwrap();
#[cfg(not(windows))]
let n = u16::try_from(errno).expect("int cast");
Error {
errno: n,
syscall: syscall_tag,
..Default::default()
}
}
#[inline]
pub fn get_errno(&self) -> E {
#[cfg(windows)]
{
E::try_from_raw(self.errno).unwrap_or(E::SUCCESS)
}
#[cfg(not(windows))]
{
SystemErrno::init(self.errno as i64).unwrap_or(SystemErrno::SUCCESS)
}
}
#[inline]
pub fn is_retry(&self) -> bool {
self.get_errno() == E::EAGAIN
}
#[inline]
pub fn oom() -> Error {
Error {
errno: E::ENOMEM as Int,
syscall: Tag::read,
..Default::default()
}
}
#[inline]
pub fn retry() -> Error {
Error {
errno: RETRY_ERRNO,
syscall: Tag::read,
..Default::default()
}
}
#[inline]
pub fn with_fd(&self, fd: Fd) -> Error {
debug_assert!(fd != Fd::INVALID);
Error {
errno: self.errno,
syscall: self.syscall,
fd,
..Default::default()
}
}
#[inline]
pub fn with_path(&self, path: &[u8]) -> Error {
Error {
errno: self.errno,
syscall: self.syscall,
path: Box::from(path),
..Default::default()
}
}
#[inline]
pub fn with_path_and_syscall(&self, path: &[u8], syscall_: Tag) -> Error {
Error {
errno: self.errno,
syscall: syscall_,
path: Box::from(path),
..Default::default()
}
}
#[inline]
pub fn with_dest(&self, dest: &[u8]) -> Error {
Error {
errno: self.errno,
syscall: self.syscall,
fd: self.fd,
#[cfg(windows)]
from_libuv: self.from_libuv,
path: self.path.clone(),
dest: Box::from(dest),
}
}
#[inline]
pub fn with_path_dest(&self, path: &[u8], dest: &[u8]) -> Error {
Error {
errno: self.errno,
syscall: self.syscall,
path: Box::from(path),
dest: Box::from(dest),
..Default::default()
}
}
pub fn without_path(&self) -> Error {
Error {
errno: self.errno,
fd: self.fd,
#[cfg(windows)]
from_libuv: self.from_libuv,
syscall: self.syscall,
path: Box::default(),
dest: Box::default(),
}
}
#[inline]
fn resolve_system_errno(&self) -> Option<SystemErrno> {
#[cfg(windows)]
{
if self.from_libuv {
let translated = crate::windows::translate_uv_error_to_e(-c_int::from(self.errno));
return Some(SystemErrno::from_raw(translated as u16));
}
E::try_from_raw(self.errno).map(|e| SystemErrno::from_raw(e as u16))
}
#[cfg(not(windows))]
{
if self.errno > 0 && self.errno < SystemErrno::MAX {
SystemErrno::init(self.errno as i64)
} else {
None
}
}
}
pub fn name(&self) -> &'static [u8] {
self.get_error_code_tag_name()
.map(|(n, _)| n.as_bytes())
.unwrap_or(b"UNKNOWN")
}
pub fn to_zig_err(&self) -> bun_core::Error {
errno_to_err(self.errno)
}
pub fn get_error_code_tag_name(&self) -> Option<(&'static str, SystemErrno)> {
let e = self.resolve_system_errno()?;
Some((<&'static str>::from(e), e))
}
pub fn msg(&self) -> Option<&'static [u8]> {
let (_code, system_errno) = self.get_error_code_tag_name()?;
Some(coreutils_error_map::COREUTILS_ERROR_MAP[system_errno].as_bytes())
}
fn fill_system_error_common(
&self,
map: &enum_map::EnumMap<SystemErrno, &'static str>,
) -> (SystemError, Option<(&'static str, &'static str)>) {
let mut err = SystemError {
errno: c_int::from(self.errno).wrapping_neg(),
syscall: BunString::static_(<&'static str>::from(self.syscall).as_bytes()),
message: BunString::empty(),
..Default::default()
};
let looked_up = self.get_error_code_tag_name().map(|(code, system_errno)| {
err.code = BunString::static_(code.as_bytes());
(code, map[system_errno])
});
if !self.path.is_empty() {
err.path = BunString::clone_utf8(&self.path);
}
if !self.dest.is_empty() {
err.dest = BunString::clone_utf8(&self.dest);
}
if let Some(valid) = fd_unwrap_valid(self.fd) {
#[cfg(windows)]
if valid.kind() == crate::FdKind::Uv {
err.fd = valid.uv();
}
#[cfg(not(windows))]
{
err.fd = valid.uv();
}
}
(err, looked_up)
}
pub fn to_shell_system_error(&self) -> SystemError {
let (mut err, looked_up) =
self.fill_system_error_common(&coreutils_error_map::COREUTILS_ERROR_MAP);
if let Some((_, label)) = looked_up {
err.message = BunString::static_(label.as_bytes());
}
err
}
pub fn to_system_error(&self) -> SystemError {
let (mut err, looked_up) = self.fill_system_error_common(&libuv_error_map::LIBUV_ERROR_MAP);
let mut message_buf = [0u8; 4096];
let pos = {
use std::io::Write as _;
let mut cursor = std::io::Cursor::new(&mut message_buf[..]);
'brk: {
if let Some((code, _)) = looked_up {
if cursor.write_all(code.as_bytes()).is_err() {
break 'brk;
}
if cursor.write_all(b": ").is_err() {
break 'brk;
}
}
let label = looked_up.map(|(_, l)| l).unwrap_or("Unknown Error");
if cursor.write_all(label.as_bytes()).is_err() {
break 'brk;
}
if cursor.write_all(b", ").is_err() {
break 'brk;
}
if cursor
.write_all(<&'static str>::from(self.syscall).as_bytes())
.is_err()
{
break 'brk;
}
if !self.path.is_empty() {
if cursor.write_all(b" '").is_err() {
break 'brk;
}
if cursor.write_all(&self.path).is_err() {
break 'brk;
}
if cursor.write_all(b"'").is_err() {
break 'brk;
}
if !self.dest.is_empty() {
if cursor.write_all(b" -> '").is_err() {
break 'brk;
}
if cursor.write_all(&self.dest).is_err() {
break 'brk;
}
if cursor.write_all(b"'").is_err() {
break 'brk;
}
}
}
}
usize::try_from(cursor.position()).expect("int cast")
};
err.message = BunString::clone_utf8(&message_buf[..pos]);
err
}
#[inline]
pub fn todo() -> Error {
if cfg!(debug_assertions) {
panic!("Error.todo() was called");
}
Error {
errno: TODO_ERRNO,
syscall: Tag::TODO,
..Default::default()
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut that = self.without_path().to_shell_system_error();
debug_assert!(that.path.tag() != bun_core::Tag::WTFStringImpl);
debug_assert!(that.dest.tag() != bun_core::Tag::WTFStringImpl);
that.path = BunString::borrow_utf8(&self.path);
that.dest = BunString::borrow_utf8(&self.dest);
debug_assert!(that.path.tag() != bun_core::Tag::WTFStringImpl);
debug_assert!(that.dest.tag() != bun_core::Tag::WTFStringImpl);
fmt::Display::fmt(&that, f)
}
}
impl bun_core::output::ErrName for Error {
fn name(&self) -> &[u8] {
Error::name(self)
}
fn as_sys_err_info(&self) -> Option<bun_core::output::SysErrInfo> {
Some(bun_core::output::SysErrInfo {
tag_name: Error::name(self),
errno: i32::from(self.errno),
syscall: <&'static str>::from(self.syscall),
})
}
}
impl bun_core::output::ErrName for &Error {
fn name(&self) -> &[u8] {
Error::name(self)
}
fn as_sys_err_info(&self) -> Option<bun_core::output::SysErrInfo> {
(**self).as_sys_err_info()
}
}
#[cfg(windows)]
pub trait ReturnCodeExt: Sized {
fn to_error(self, syscall_tag: Tag) -> Option<Error>;
#[inline]
fn to_result(self, syscall_tag: Tag) -> crate::Result<()> {
match self.to_error(syscall_tag) {
Some(e) => Err(e),
None => Ok(()),
}
}
#[inline]
fn as_err(self, syscall_tag: Tag) -> Option<Error> {
self.to_error(syscall_tag)
}
fn err_enum_e(self) -> Option<crate::E>;
}
#[cfg(windows)]
impl ReturnCodeExt for crate::windows::libuv::ReturnCode {
#[inline]
fn to_error(self, syscall_tag: Tag) -> Option<Error> {
Error::from_uv_rc(self, syscall_tag)
}
#[inline]
fn err_enum_e(self) -> Option<crate::E> {
if self.int() < 0 {
Some(crate::windows::translate_uv_error_to_e(self.int()))
} else {
None
}
}
}
#[cfg(windows)]
impl ReturnCodeExt for crate::windows::libuv::ReturnCodeI64 {
#[inline]
fn to_error(self, syscall_tag: Tag) -> Option<Error> {
Error::from_uv_rc64(self, syscall_tag)
}
#[inline]
fn err_enum_e(self) -> Option<crate::E> {
if self.int() < 0 {
Some(crate::windows::translate_uv_error_to_e(
self.int() as core::ffi::c_int
))
} else {
None
}
}
}