use super::{Gid, Result, Uid};
use crate::io::Fd;
use crate::path::{Path, PathBuf};
use crate::str::UnixStr;
use bitflags::bitflags;
use enumset::{EnumSet, EnumSetType};
use std::ffi::CStr;
use std::fmt::Debug;
use std::io::SeekFrom;
use std::rc::Rc;
#[cfg(unix)]
const RAW_AT_FDCWD: i32 = libc::AT_FDCWD;
#[cfg(not(unix))]
const RAW_AT_FDCWD: i32 = -100;
pub const AT_FDCWD: Fd = Fd(RAW_AT_FDCWD);
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct DirEntry<'a> {
pub name: &'a UnixStr,
}
pub trait Dir: Debug {
fn next(&mut self) -> Result<Option<DirEntry<'_>>>;
}
#[cfg(unix)]
type RawModeDef = libc::mode_t;
#[cfg(not(unix))]
type RawModeDef = u32;
pub type RawMode = RawModeDef;
#[derive(Copy, Clone, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Mode(RawMode);
bitflags! {
impl Mode: RawMode {
const USER_READ = 0o400;
const USER_WRITE = 0o200;
const USER_EXEC = 0o100;
const USER_ALL = 0o700;
const GROUP_READ = 0o040;
const GROUP_WRITE = 0o020;
const GROUP_EXEC = 0o010;
const GROUP_ALL = 0o070;
const OTHER_READ = 0o004;
const OTHER_WRITE = 0o002;
const OTHER_EXEC = 0o001;
const OTHER_ALL = 0o007;
const ALL_READ = 0o444;
const ALL_WRITE = 0o222;
const ALL_EXEC = 0o111;
const ALL_9 = 0o777;
const SET_USER_ID = 0o4000;
const SET_GROUP_ID = 0o2000;
const STICKY = 0o1000;
}
}
impl Debug for Mode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Mode({:#o})", self.0)
}
}
impl Default for Mode {
fn default() -> Mode {
Mode(0o644)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum FileType {
Regular,
Directory,
Symlink,
Fifo,
BlockDevice,
CharacterDevice,
Socket,
Other,
}
pub trait Stat {
#[must_use]
fn dev(&self) -> u64;
#[must_use]
fn ino(&self) -> u64;
#[must_use]
fn mode(&self) -> Mode;
#[must_use]
fn r#type(&self) -> FileType;
#[must_use]
fn nlink(&self) -> u64;
#[must_use]
fn uid(&self) -> Uid;
#[must_use]
fn gid(&self) -> Gid;
#[must_use]
fn size(&self) -> u64;
#[inline(always)]
#[must_use]
fn identity(&self) -> (u64, u64) {
(self.dev(), self.ino())
}
#[inline(always)]
#[must_use]
fn is_regular_file(&self) -> bool {
self.r#type() == FileType::Regular
}
#[inline(always)]
#[must_use]
fn is_directory(&self) -> bool {
self.r#type() == FileType::Directory
}
#[inline(always)]
#[must_use]
fn is_symlink(&self) -> bool {
self.r#type() == FileType::Symlink
}
#[inline(always)]
#[must_use]
fn is_fifo(&self) -> bool {
self.r#type() == FileType::Fifo
}
#[inline(always)]
#[must_use]
fn is_block_device(&self) -> bool {
self.r#type() == FileType::BlockDevice
}
#[inline(always)]
#[must_use]
fn is_character_device(&self) -> bool {
self.r#type() == FileType::CharacterDevice
}
#[inline(always)]
#[must_use]
fn is_socket(&self) -> bool {
self.r#type() == FileType::Socket
}
}
pub trait Fstat {
type Stat: Stat + Clone + Debug;
fn fstat(&self, fd: Fd) -> Result<Self::Stat>;
fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Self::Stat>;
#[must_use]
fn is_directory(&self, path: &CStr) -> bool {
self.fstatat(AT_FDCWD, path, true)
.is_ok_and(|stat| stat.is_directory())
}
#[must_use]
fn fd_is_pipe(&self, fd: Fd) -> bool {
self.fstat(fd).is_ok_and(|stat| stat.is_fifo())
}
}
impl<S: Fstat> Fstat for Rc<S> {
type Stat = S::Stat;
#[inline]
fn fstat(&self, fd: Fd) -> Result<S::Stat> {
(self as &S).fstat(fd)
}
#[inline]
fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<S::Stat> {
(self as &S).fstatat(dir_fd, path, follow_symlinks)
}
#[inline]
fn is_directory(&self, path: &CStr) -> bool {
(self as &S).is_directory(path)
}
#[inline]
fn fd_is_pipe(&self, fd: Fd) -> bool {
(self as &S).fd_is_pipe(fd)
}
}
pub trait IsExecutableFile {
#[must_use]
fn is_executable_file(&self, path: &CStr) -> bool;
}
impl<S: IsExecutableFile> IsExecutableFile for Rc<S> {
#[inline]
fn is_executable_file(&self, path: &CStr) -> bool {
(self as &S).is_executable_file(path)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum OfdAccess {
ReadOnly,
WriteOnly,
ReadWrite,
Exec,
Search,
}
#[derive(Debug, EnumSetType, Hash)]
#[non_exhaustive]
pub enum OpenFlag {
Append,
CloseOnExec,
Create,
Directory,
Exclusive,
NoCtty,
NoFollow,
NonBlock,
Sync,
Truncate,
}
pub trait Open {
fn open(
&self,
path: &CStr,
access: OfdAccess,
flags: EnumSet<OpenFlag>,
mode: Mode,
) -> impl Future<Output = Result<Fd>> + use<Self>;
fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd>;
fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<Self>>;
fn opendir(&self, path: &CStr) -> Result<impl Dir + use<Self>>;
}
impl<S: Open> Open for Rc<S> {
#[inline]
fn open(
&self,
path: &CStr,
access: OfdAccess,
flags: EnumSet<OpenFlag>,
mode: Mode,
) -> impl Future<Output = Result<Fd>> + use<S> {
(self as &S).open(path, access, flags, mode)
}
#[inline]
fn open_tmpfile(&self, parent_dir: &Path) -> Result<Fd> {
(self as &S).open_tmpfile(parent_dir)
}
#[inline]
fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<S>> {
(self as &S).fdopendir(fd)
}
#[inline]
fn opendir(&self, path: &CStr) -> Result<impl Dir + use<S>> {
(self as &S).opendir(path)
}
}
pub trait Seek {
fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64>;
}
impl<S: Seek> Seek for Rc<S> {
#[inline]
fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
(self as &S).lseek(fd, position)
}
}
pub trait Umask {
fn umask(&self, new_mask: Mode) -> Mode;
}
impl<S: Umask> Umask for Rc<S> {
#[inline]
fn umask(&self, new_mask: Mode) -> Mode {
(self as &S).umask(new_mask)
}
}
pub trait GetCwd {
fn getcwd(&self) -> Result<PathBuf>;
}
impl<S: GetCwd> GetCwd for Rc<S> {
#[inline]
fn getcwd(&self) -> Result<PathBuf> {
(self as &S).getcwd()
}
}
pub trait Chdir {
fn chdir(&self, path: &CStr) -> Result<()>;
}
impl<S: Chdir> Chdir for Rc<S> {
#[inline]
fn chdir(&self, path: &CStr) -> Result<()> {
(self as &S).chdir(path)
}
}