use bun_core::ZStr;
#[cfg(unix)]
use bun_sys::FdExt;
use bun_sys::{self, Fd, Mode};
pub trait OpenForWritingInput {
fn open_for_writing_result(
&self,
dir: Fd,
input_flags: i32,
mode: Mode,
is_nonblocking: &mut bool,
openat: &dyn Fn(Fd, &ZStr, i32, Mode) -> bun_sys::Result<Fd>,
) -> bun_sys::Result<Fd>;
}
impl OpenForWritingInput for crate::PathOrFileDescriptor {
fn open_for_writing_result(
&self,
dir: Fd,
input_flags: i32,
mode: Mode,
is_nonblocking: &mut bool,
_openat: &dyn Fn(Fd, &ZStr, i32, Mode) -> bun_sys::Result<Fd>,
) -> bun_sys::Result<Fd> {
use crate::PathOrFileDescriptor::*;
match self {
Path(path) => {
*is_nonblocking = true;
bun_sys::openat_a(dir, path.slice(), input_flags, mode)
}
Fd(fd_) => bun_sys::dup_with_flags(*fd_, 0),
}
}
}
impl OpenForWritingInput for &ZStr {
fn open_for_writing_result(
&self,
dir: Fd,
input_flags: i32,
mode: Mode,
_is_nonblocking: &mut bool,
openat: &dyn Fn(Fd, &ZStr, i32, Mode) -> bun_sys::Result<Fd>,
) -> bun_sys::Result<Fd> {
openat(dir, self, input_flags, mode)
}
}
impl OpenForWritingInput for &mut ZStr {
fn open_for_writing_result(
&self,
dir: Fd,
input_flags: i32,
mode: Mode,
_is_nonblocking: &mut bool,
openat: &dyn Fn(Fd, &ZStr, i32, Mode) -> bun_sys::Result<Fd>,
) -> bun_sys::Result<Fd> {
openat(dir, self, input_flags, mode)
}
}
pub fn open_for_writing<P, C>(
dir: Fd,
input_path: &P,
input_flags: i32,
mode: Mode,
pollable: &mut bool,
is_socket: &mut bool,
force_sync: bool,
out_nonblocking: &mut bool,
ctx: C,
on_force_sync_or_isa_tty: fn(C),
is_pollable: fn(mode: Mode) -> bool,
) -> bun_sys::Result<Fd>
where
P: OpenForWritingInput,
{
open_for_writing_impl(
dir,
input_path,
input_flags,
mode,
pollable,
is_socket,
force_sync,
out_nonblocking,
ctx,
on_force_sync_or_isa_tty,
is_pollable,
bun_sys::openat,
)
}
pub fn open_for_writing_impl<P, C>(
dir: Fd,
input_path: &P,
input_flags: i32,
mode: Mode,
pollable: &mut bool,
is_socket: &mut bool,
force_sync: bool,
out_nonblocking: &mut bool,
ctx: C,
on_force_sync_or_isa_tty: fn(C),
is_pollable: fn(mode: Mode) -> bool,
openat: fn(dir: Fd, path: &ZStr, flags: i32, mode: Mode) -> bun_sys::Result<Fd>,
) -> bun_sys::Result<Fd>
where
P: OpenForWritingInput,
{
#[cfg(windows)]
{
let _ = (
is_socket,
out_nonblocking,
ctx,
on_force_sync_or_isa_tty,
is_pollable,
);
}
#[cfg(unix)]
let mut isatty = false;
let mut is_nonblocking = false;
let result =
input_path.open_for_writing_result(dir, input_flags, mode, &mut is_nonblocking, &openat);
let fd = result?;
#[cfg(unix)]
{
match bun_sys::fstat(fd) {
Err(err) => {
fd.close();
return Err(err);
}
Ok(stat) => {
*pollable = is_pollable(stat.st_mode as Mode);
if !*pollable {
isatty = bun_sys::isatty(fd);
}
if isatty {
*pollable = true;
}
*is_socket = bun_sys::S::ISSOCK(stat.st_mode as Mode);
if force_sync || isatty {
let _ = bun_sys::update_nonblocking(fd, false);
is_nonblocking = false;
on_force_sync_or_isa_tty(ctx);
} else if !is_nonblocking {
let flags = match bun_sys::get_fcntl_flags(fd) {
Ok(flags) => flags,
Err(err) => {
fd.close();
return Err(err);
}
};
is_nonblocking = (flags as i32 & bun_sys::O::NONBLOCK) != 0;
if !is_nonblocking {
if bun_sys::set_nonblocking(fd).is_ok() {
is_nonblocking = true;
}
}
}
*out_nonblocking = is_nonblocking && *pollable;
}
}
return Ok(fd);
}
#[cfg(windows)]
{
*pollable = (bun_sys::windows::GetFileType(fd.native()) & bun_sys::windows::FILE_TYPE_PIPE)
!= 0
&& !force_sync;
return Ok(fd);
}
}