Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions src/errno/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ macro_rules! impl_get_errno_libc {
// against the type's own all-ones value instead (== -1 for
// signed, == MAX for unsigned — both are libc's failure rc).
if self == !(0 as $t) {
u16::try_from($crate::posix::errno())
.map_or($crate::E::EUNKNOWN, $crate::E::from_raw)
$crate::last_error()
} else {
$crate::E::SUCCESS
}
Expand Down Expand Up @@ -234,26 +233,28 @@ pub mod posix {
pub use bun_core::ffi::errno;
}

/// Errno extraction strategy, modeled as a trait with per-type impls — call
/// as `rc.get_errno()` or `get_errno(rc)`.
///
/// The trait declaration is target-independent; each per-OS module supplies its
/// own `impl GetErrno for {i32,u32,isize,usize,...}` (Linux decodes raw-syscall
/// `-errno` from `usize`, Darwin/FreeBSD read thread-local errno on `-1`,
/// Windows ignores `rc` and reads `GetLastError()`/`WSAGetLastError()`).
/// Decode a POSIX syscall return value: `rc.get_errno()` / `get_errno(rc)` is
/// `SUCCESS` unless `rc` is the failure sentinel, in which case it is the errno
/// (Linux raw syscalls decode `-errno` from `usize`; libc wrappers read the
/// thread-local errno on `-1`).
#[cfg(not(windows))]
pub trait GetErrno: Copy {
fn get_errno(self) -> E;
}

// Free-function shim over the trait method. POSIX-only:
// Windows defines its own divergent `get_errno<T>(_rc)` (no trait bound, reads
// GetLastError/WSAGetLastError) in windows_errno.rs.
#[cfg(not(windows))]
#[inline]
pub fn get_errno<T: GetErrno>(rc: T) -> E {
rc.get_errno()
}

/// The thread-local errno as `E` (`SUCCESS` when it is 0).
#[cfg(not(windows))]
#[inline]
pub fn last_error() -> E {
u16::try_from(posix::errno()).map_or(E::EUNKNOWN, E::from_raw)
}

/// Decode a Node-style **negated** errno (`c_int`, as written by
/// `Error::to_system_error`) back into an `E`. The input crosses FFI
/// (BunObject.cpp `SystemError__*`) and is NOT trusted to be a declared
Expand Down Expand Up @@ -299,7 +300,7 @@ impl SystemErrno {
// `i64` covers every concrete call site (errno-range values).
//
// Windows defines its own `init<C: SystemErrnoInit>` (typed dispatch over
// DWORD/c_int/Win32Error) in windows_errno.rs, so this impl is POSIX-only.
// i64/DWORD/c_int) in windows_errno.rs, so this impl is POSIX-only.
pub fn init(code: i64) -> Option<SystemErrno> {
u16::try_from(code.unsigned_abs())
.ok()
Expand Down
122 changes: 33 additions & 89 deletions src/errno/windows_errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ pub enum E {
impl E {
/// An undeclared discriminant maps to `UNKNOWN`.
#[inline]
pub(crate) const fn from_raw(n: u16) -> Self {
pub const fn from_raw(n: u16) -> Self {
match Self::from_repr(n) {
Some(e) => e,
None => Self::UNKNOWN,
Expand Down Expand Up @@ -350,20 +350,6 @@ pub mod posix {
/// Uppercase re-export so `bun_errno::S::IFDIR` compiles cross-platform.
pub use self::s as S;

use super::GetErrno;

// Windows errno comes from `GetLastError()` regardless of `rc`, so every impl
// ignores `self`. Kept to the same concrete-type set as POSIX — a blanket impl
// would shadow `bun_sys::Error::get_errno` (inherent method) via autoref.
macro_rules! impl_win_get_errno {
($($t:ty),*) => {$(
impl GetErrno for $t {
#[inline] fn get_errno(self) -> E { get_errno(self) }
}
)*};
}
impl_win_get_errno!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);

// ──────────────────────────────────────────────────────────────────────────
// S — file mode bits
// ──────────────────────────────────────────────────────────────────────────
Expand All @@ -374,25 +360,18 @@ impl_win_get_errno!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
pub use bun_core::S as s;

// ──────────────────────────────────────────────────────────────────────────
// getErrno
// last_error
// ──────────────────────────────────────────────────────────────────────────

/// `get_errno(rc)` — `rc` is ignored;
/// NTSTATUS callers use `windows::translate_ntstatus_to_errno` directly.
pub fn get_errno<T>(_rc: T) -> E {
if let Some(sys) = Win32Error::get().to_system_errno() {
return sys.to_e();
}

// `wsa_get_last_error()` returns `Option<SystemErrno>` (already routed
// through the Win32Error→errno switch). An unmapped non-zero WSA code
// yields `None` there and falls through to `SUCCESS` — it must NOT surface
// as `E::UNKNOWN` (which `Win32ErrorExt::to_e`'s `unwrap_or` would do).
if let Some(wsa) = windows::wsa_get_last_error() {
return wsa.to_e();
/// `GetLastError()` as `E`: `SUCCESS` when no code is recorded, else
/// `Win32ErrorExt::to_e`. For the error of a call known to have failed use
/// `bun_sys::Error::from_win32` / `bun_sys::windows::last_system_errno`.
#[inline]
pub fn last_error() -> E {
match Win32Error::get() {
Win32Error::SUCCESS => E::SUCCESS,
code => code.to_e(),
}

E::SUCCESS
}

// ──────────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -560,16 +539,17 @@ pub enum SystemErrno {
}} // ← UV_* tail appended by `for_each_uv_errno!`

/// Type-dispatch shim for `SystemErrno::init`.
/// Covers every concrete type the codebase actually passes — `i64` (shared
/// `Error.rs` paths), `u32`/`DWORD` (`GetLastError()`), `c_int` (libuv rc),
/// `u16`, and `Win32Error`.
/// Covers every concrete type the codebase actually passes — `i64`
/// (POSIX-shaped shared call sites), `u32`/`DWORD` (a Win32/WSA code carried
/// as an integer), and `c_int` (`Bun__errnoName`, which may receive a libuv
/// code). A typed `Win32Error` uses `Win32ErrorExt` instead.
pub trait SystemErrnoInit {
fn into_system_errno(self) -> Option<SystemErrno>;
}
impl SystemErrnoInit for i64 {
#[inline]
fn into_system_errno(self) -> Option<SystemErrno> {
// Only `u16` / positive `c_int` inputs enter the Win32/uv mapping
// Only `u32` / positive `c_int` inputs enter the Win32/uv mapping
// branch; `i64` is a direct discriminant cast, NOT the Win32Error
// mapper. Routing i64 through `init_c_int` would mis-map e.g. 13 →
// EINVAL (Win32 ERROR_INVALID_DATA) instead of EACCES (discriminant 13).
Expand Down Expand Up @@ -601,19 +581,7 @@ impl SystemErrnoInit for u32 {
// codes and some installer/WinHTTP errors exceed 0xFFFF. Those are
// intentionally unmapped → None. Codes that DO fit u16 route via
// the Win32Error→errno table.
u16::try_from(self).ok().and_then(SystemErrno::init_u16)
}
}
impl SystemErrnoInit for u16 {
#[inline]
fn into_system_errno(self) -> Option<SystemErrno> {
SystemErrno::init_u16(self)
}
}
impl SystemErrnoInit for Win32Error {
#[inline]
fn into_system_errno(self) -> Option<SystemErrno> {
SystemErrno::init_win32_error(self)
u16::try_from(self).ok().and_then(SystemErrno::init_numeric)
}
}

Expand All @@ -631,19 +599,14 @@ impl SystemErrno {
}

/// Cross-platform `SystemErrno::init` — POSIX targets define a single
/// `init(i64)`; Windows splits it into typed entry points (`init_u16` /
/// `init_c_int` / `init_win32_error`). Re-unified here behind `SystemErrnoInit` so
/// shared call sites can keep writing `SystemErrno::init(code)`.
/// `init(i64)`; Windows dispatches on the integer type via
/// `SystemErrnoInit` so shared call sites can keep writing
/// `SystemErrno::init(code)`.
#[inline]
pub fn init<C: SystemErrnoInit>(code: C) -> Option<SystemErrno> {
code.into_system_errno()
}

/// `init(code: u16)` — Win32/WSA error codes and negated-uv codes encoded as u16.
pub(crate) fn init_u16(code: u16) -> Option<SystemErrno> {
Self::init_numeric(code)
}

/// `init(code: c_int)` — same as u16 path for positives; negatives are negated and retried.
pub(crate) fn init_c_int(code: c_int) -> Option<SystemErrno> {
if code > 0 {
Expand Down Expand Up @@ -783,20 +746,16 @@ impl SystemErrno {
W::META_EXPANSION_TOO_LONG => SystemErrno::E2BIG,
W::WSAESOCKTNOSUPPORT => SystemErrno::ESOCKTNOSUPPORT,
W::DELETE_PENDING => SystemErrno::EBUSY,
W::BAD_EXE_FORMAT => SystemErrno::EFTYPE,
_ => return None,
})
}
}

/// Thin typed adapter over the canonical row table in `bun_libuv_sys`.
/// A negative libuv return code → `E`; a code libuv does not define is `UNKNOWN`.
pub fn translate_uv_error_to_e(code: c_int) -> E {
uv::uv_err_to_e_discriminant(code)
.and_then(E::try_from_raw)
.or_else(|| {
u16::try_from(code.wrapping_neg())
.ok()
.and_then(E::try_from_raw)
})
.unwrap_or(E::UNKNOWN)
}

Expand Down Expand Up @@ -829,13 +788,13 @@ pub mod uv_e {
// ──────────────────────────────────────────────────────────────────────────
// `windows` — Win32Error / NTSTATUS / kernel32 surface moved DOWN from
// `bun_sys::windows` (cycle-break per PORTING.md §Dep-cycle fixes). Only the
// subset referenced by `SystemErrno::init` / `get_errno` is mirrored; the full
// subset referenced by `SystemErrno::init` / `last_error` is mirrored; the full
// 1100-variant table stays in `bun_sys::windows` and re-exports this newtype.
// ──────────────────────────────────────────────────────────────────────────
pub mod windows {
use super::{E, SystemErrno};

/// `enum(u16) Win32Error` — newtype over `GetLastError()`'s low word.
/// `enum(u16) Win32Error` — newtype over `GetLastError()` (see `Win32Error::get`).
/// Re-exported from the tier-0 `bun_windows_sys` leaf crate (no cycle:
/// that crate has zero workspace deps), so this module and
/// `bun_sys::windows` share one nominal type.
Expand All @@ -844,42 +803,27 @@ pub mod windows {
/// `NTSTATUS` — `enum(u32) { …, _ }`. Same provenance as `Win32Error`.
pub use bun_windows_sys::NTSTATUS;

use bun_windows_sys::ws2_32::WSAGetLastError;

/// Extension trait for `Win32Error` → `SystemErrno`/`E` mapping.
/// `bun_windows_sys` is tier-0 and cannot name `SystemErrno`, so
/// `to_system_errno()` surfaces here as an extension method instead.
/// `bun_windows_sys` is tier-0 and cannot name `SystemErrno`, so the
/// mapping surfaces here as extension methods instead.
pub trait Win32ErrorExt: Copy {
fn to_system_errno(self) -> Option<SystemErrno>;
/// Convenience: Win32 error → `E`, falling back to `E::UNKNOWN` for
/// codes not in the Win32→errno table.
///
/// **Note:** NOT appropriate where unmapped codes must fall through
/// to `SUCCESS` (e.g. the WSA path of `get_errno`); those callers
/// must use `to_system_errno()` and choose their own fallback.
/// The errno for a failed call whose `GetLastError()` is `self`: its row
/// in the Win32→errno table (`init_win32_error`), else `EUNKNOWN` —
/// including `SUCCESS`, a failure that set no code. Compare against
/// `Win32Error::SUCCESS` first when the call may have succeeded.
fn to_system_errno(self) -> SystemErrno;
#[inline]
fn to_e(self) -> E {
self.to_system_errno()
.map(SystemErrno::to_e)
.unwrap_or(E::UNKNOWN)
self.to_system_errno().to_e()
}
}
impl Win32ErrorExt for Win32Error {
#[inline]
fn to_system_errno(self) -> Option<SystemErrno> {
SystemErrno::init_win32_error(self)
fn to_system_errno(self) -> SystemErrno {
SystemErrno::init_win32_error(self).unwrap_or(SystemErrno::EUNKNOWN)
}
}

/// Feeds the raw WSA code (`c_int`) through the Win32→errno switch.
/// Returns `Some(SUCCESS)` for `0` and `None` for any non-zero code with
/// no mapping (e.g. `WSANOTINITIALISED`/`WSAEDISCON`); callers that need
/// a success-on-unmapped fallthrough (`getErrno`) rely on that `None`.
#[inline]
pub(crate) fn wsa_get_last_error() -> Option<SystemErrno> {
SystemErrno::init_c_int(WSAGetLastError())
}

/// Moved DOWN so `bun_errno` owns the only NTSTATUS→`E` mapping (cycle-break).
pub fn translate_ntstatus_to_errno(err: NTSTATUS) -> E {
match err {
Expand Down
Loading
Loading