yash-env 0.13.2

Yash shell execution environment interface
Documentation
// This file is part of yash, an extended POSIX shell.
// Copyright (C) 2025 WATANABE Yuki
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

//! Items about I/O operations

use super::Result;
use super::file_system::OfdAccess;
use crate::io::Fd;
use enumset::{EnumSet, EnumSetType};
use std::rc::Rc;

/// Attributes for file descriptors
#[derive(Debug, EnumSetType, Hash)]
#[non_exhaustive]
pub enum FdFlag {
    /// Close the file descriptor upon execution of an exec family function
    CloseOnExec,
    // TODO CloseOnFork,
}

/// Trait for closing file descriptors
pub trait Close {
    /// Closes a file descriptor.
    ///
    /// This is a thin wrapper around the [`close` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html).
    ///
    /// If successful, returns `Ok(())`. On error, returns `Err(_)`.
    /// This function returns `Ok(())` when the FD is already closed, which is
    /// different from the behavior of the underlying system call.
    fn close(&self, fd: Fd) -> Result<()>;
}

/// Delegates the `Close` trait to the contained instance of `S`
impl<S: Close> Close for Rc<S> {
    #[inline]
    fn close(&self, fd: Fd) -> Result<()> {
        (self as &S).close(fd)
    }
}

/// Trait for creating pipes
///
/// This trait declares the `pipe` method, which creates an unnamed pipe. This
/// is a wrapper around the `pipe` system call.
pub trait Pipe {
    /// Creates an unnamed pipe.
    ///
    /// This is a thin wrapper around the [`pipe` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html).
    /// If successful, returns the reading and writing ends of the pipe.
    fn pipe(&self) -> Result<(Fd, Fd)>;
}

/// Delegates the `Pipe` trait to the contained instance of `S`
impl<S: Pipe> Pipe for Rc<S> {
    #[inline]
    fn pipe(&self) -> Result<(Fd, Fd)> {
        (self as &S).pipe()
    }
}

/// Trait for duplicating file descriptors
///
/// This trait declares the `dup` and `dup2` methods, which duplicate file
/// descriptors.
pub trait Dup {
    /// Duplicates a file descriptor.
    ///
    /// This is a thin wrapper around the [`fcntl` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html)
    /// that opens a new FD that shares the open file description with `from`.
    /// The new FD will be the minimum unused FD not less than `to_min`. The
    /// `flags` are set to the new FD.
    ///
    /// If successful, returns `Ok(new_fd)`. On error, returns `Err(_)`.
    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd>;

    /// Duplicates a file descriptor.
    ///
    /// This is a thin wrapper around the [`dup2` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html).
    /// If successful, returns `Ok(to)`. On error, returns `Err(_)`.
    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd>;
}

/// Delegates the `Dup` trait to the contained instance of `S`
impl<S: Dup> Dup for Rc<S> {
    #[inline]
    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
        (self as &S).dup(from, to_min, flags)
    }
    #[inline]
    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
        (self as &S).dup2(from, to)
    }
}

/// Trait for `fcntl`-related operations
///
/// This trait declares methods related to the [`fcntl` system
/// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html)
/// for manipulating file descriptors and open file descriptions.
pub trait Fcntl {
    /// Returns the open file description access mode.
    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess>;

    /// Gets and sets the non-blocking mode for the open file description.
    ///
    /// This function sets the non-blocking mode to the given value and returns
    /// the previous mode.
    fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool>;

    /// Returns the attributes for the file descriptor.
    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>>;

    /// Sets attributes for the file descriptor.
    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()>;
}

/// Delegates the `Fcntl` trait to the contained instance of `S`
impl<S: Fcntl> Fcntl for Rc<S> {
    #[inline]
    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
        (self as &S).ofd_access(fd)
    }
    #[inline]
    fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
        (self as &S).get_and_set_nonblocking(fd, nonblocking)
    }
    #[inline]
    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
        (self as &S).fcntl_getfd(fd)
    }
    #[inline]
    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
        (self as &S).fcntl_setfd(fd, flags)
    }
}

/// Trait for reading from file descriptors
pub trait Read {
    /// Reads from the file descriptor.
    ///
    /// This is a thin wrapper around the [`read` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html).
    /// If successful, returns the number of bytes read.
    ///
    /// This function may perform blocking I/O, specifically if the `O_NONBLOCK`
    /// flag is not set for the FD. The return type is a `Future` to support
    /// simulating blocking I/O in [virtual systems](super::virtual). In the
    /// [real system](super::real), this function does not work asynchronously
    /// and returns a ready `Future` with the result of the underlying system
    /// call. See the [module-level documentation](super) for details.
    ///
    /// The implementation for [`Rc<Concurrent<_>>`](super::Concurrent) provides
    /// asynchronous reading by using non-blocking I/O, which allows concurrent
    /// multitasking in `async` function contexts.
    fn read<'a>(
        &self,
        fd: Fd,
        buffer: &'a mut [u8],
    ) -> impl Future<Output = Result<usize>> + use<'a, Self>;
}

/// Delegates the `Read` trait to the contained instance of `S`
impl<S: Read> Read for Rc<S> {
    #[inline]
    fn read<'a>(
        &self,
        fd: Fd,
        buffer: &'a mut [u8],
    ) -> impl Future<Output = Result<usize>> + use<'a, S> {
        (self as &S).read(fd, buffer)
    }
}

/// Trait for writing to file descriptors
pub trait Write {
    /// Writes to the file descriptor.
    ///
    /// This is a thin wrapper around the [`write` system
    /// call](https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html).
    /// If successful, returns the number of bytes written.
    ///
    /// This function may write only part of the `buffer` and block if the
    /// `O_NONBLOCK` flag is not set for the FD. The return type is a `Future`
    /// to support simulating blocking I/O in [virtual systems](super::virtual).
    /// In the [real system](super::real), this function does not work
    /// asynchronously and returns a ready `Future` with the result of the
    /// underlying system call. See the [module-level documentation](super) for
    /// details.
    ///
    /// The implementation for [`Rc<Concurrent<_>>`](super::Concurrent) provides
    /// asynchronous writing by using non-blocking I/O, which allows concurrent
    /// multitasking in `async` function contexts. Use
    /// [`Concurrent::write_all`](super::Concurrent::write_all) to write the
    /// entire buffer.
    fn write<'a>(
        &self,
        fd: Fd,
        buffer: &'a [u8],
    ) -> impl Future<Output = Result<usize>> + use<'a, Self>;
}

/// Delegates the `Write` trait to the contained instance of `S`
impl<S: Write> Write for Rc<S> {
    #[inline]
    fn write<'a>(
        &self,
        fd: Fd,
        buffer: &'a [u8],
    ) -> impl Future<Output = Result<usize>> + use<'a, S> {
        (self as &S).write(fd, buffer)
    }
}