compio-fs 0.12.0-rc.1

Filesystem IO for compio
Documentation
pub use std::fs::Permissions;
use std::{
    hash::Hash,
    io,
    os::{
        fd::AsFd,
        unix::prelude::{FileTypeExt, MetadataExt, PermissionsExt},
    },
    path::Path,
    time::{Duration, SystemTime},
};

use compio_buf::{BufResult, IntoInner};
#[cfg(dirfd)]
use compio_driver::ToSharedFd;
use compio_driver::op::{CurrentDir, PathStat, Stat};
use compio_runtime::ResumeUnwind;
use rustix::fs::FileType as FileTypeInner;

use crate::path_string;

async fn metadata_impl(
    dir: impl AsFd + 'static,
    path: impl AsRef<Path>,
    follow_symlink: bool,
) -> io::Result<Metadata> {
    let path = path_string(path)?;
    let op = PathStat::new(dir, path, follow_symlink);
    let BufResult(res, op) = compio_runtime::submit(op).await;
    res.map(|_| Metadata::from_stat(op.into_inner()))
}

pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
    metadata_impl(CurrentDir, path, true).await
}

pub async fn symlink_metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
    metadata_impl(CurrentDir, path, false).await
}

#[cfg(dirfd)]
pub async fn metadata_at(dir: &crate::File, path: impl AsRef<Path>) -> io::Result<Metadata> {
    metadata_impl(dir.to_shared_fd(), path, true).await
}

#[cfg(dirfd)]
pub async fn symlink_metadata_at(
    dir: &crate::File,
    path: impl AsRef<Path>,
) -> io::Result<Metadata> {
    metadata_impl(dir.to_shared_fd(), path, false).await
}

pub async fn set_permissions(path: impl AsRef<Path>, perm: Permissions) -> io::Result<()> {
    let path = path.as_ref().to_path_buf();
    compio_runtime::spawn_blocking(move || std::fs::set_permissions(path, perm))
        .await
        .resume_unwind()
        .expect("shouldn't be cancelled")
}

#[derive(Clone)]
pub struct Metadata(pub(crate) Stat);

impl Metadata {
    /// Create from [`Stat`].
    pub fn from_stat(stat: Stat) -> Self {
        Self(stat)
    }

    pub fn file_type(&self) -> FileType {
        FileType(FileTypeInner::from_raw_mode(self.0.st_mode as _))
    }

    pub fn is_dir(&self) -> bool {
        self.file_type().is_dir()
    }

    pub fn is_file(&self) -> bool {
        self.file_type().is_file()
    }

    pub fn is_symlink(&self) -> bool {
        self.file_type().is_symlink()
    }

    #[allow(clippy::len_without_is_empty)]
    pub fn len(&self) -> u64 {
        self.0.st_size as _
    }

    pub fn permissions(&self) -> Permissions {
        Permissions::from_mode(self.0.st_mode as _)
    }

    pub fn modified(&self) -> io::Result<SystemTime> {
        Ok(SystemTime::UNIX_EPOCH
            + Duration::from_secs(self.0.st_mtime as _)
            + Duration::from_nanos(self.0.st_mtime_nsec as _))
    }

    pub fn accessed(&self) -> io::Result<SystemTime> {
        Ok(SystemTime::UNIX_EPOCH
            + Duration::from_secs(self.0.st_atime as _)
            + Duration::from_nanos(self.0.st_atime_nsec as _))
    }

    #[cfg(not(noctime))]
    pub fn created(&self) -> io::Result<SystemTime> {
        // We've assigned btime field to ctime.
        Ok(SystemTime::UNIX_EPOCH
            + Duration::from_secs(self.0.st_ctime as _)
            + Duration::from_nanos(self.0.st_ctime_nsec as _))
    }

    #[cfg(noctime)]
    pub fn created(&self) -> io::Result<SystemTime> {
        Ok(SystemTime::UNIX_EPOCH
            + Duration::from_secs(self.0.st_birthtime as _)
            + Duration::from_nanos(self.0.st_birthtime_nsec as _))
    }
}

impl MetadataExt for Metadata {
    fn dev(&self) -> u64 {
        self.0.st_dev as _
    }

    fn ino(&self) -> u64 {
        self.0.st_ino as _
    }

    fn mode(&self) -> u32 {
        self.0.st_mode as _
    }

    fn nlink(&self) -> u64 {
        self.0.st_nlink as _
    }

    fn uid(&self) -> u32 {
        self.0.st_uid as _
    }

    fn gid(&self) -> u32 {
        self.0.st_gid as _
    }

    fn rdev(&self) -> u64 {
        self.0.st_rdev as _
    }

    fn size(&self) -> u64 {
        self.0.st_size as _
    }

    fn atime(&self) -> i64 {
        self.0.st_atime as _
    }

    fn atime_nsec(&self) -> i64 {
        self.0.st_atime_nsec as _
    }

    fn mtime(&self) -> i64 {
        self.0.st_mtime as _
    }

    fn mtime_nsec(&self) -> i64 {
        self.0.st_mtime_nsec as _
    }

    fn ctime(&self) -> i64 {
        self.0.st_ctime as _
    }

    fn ctime_nsec(&self) -> i64 {
        self.0.st_ctime_nsec as _
    }

    fn blksize(&self) -> u64 {
        self.0.st_blksize as _
    }

    fn blocks(&self) -> u64 {
        self.0.st_blocks as _
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct FileType(pub(crate) FileTypeInner);

impl Hash for FileType {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.as_raw_mode().hash(state);
    }
}

impl FileType {
    pub fn is_dir(&self) -> bool {
        self.0.is_dir()
    }

    pub fn is_file(&self) -> bool {
        self.0.is_file()
    }

    pub fn is_symlink(&self) -> bool {
        self.0.is_symlink()
    }
}

impl FileTypeExt for FileType {
    fn is_block_device(&self) -> bool {
        self.0.is_block_device()
    }

    fn is_char_device(&self) -> bool {
        self.0.is_char_device()
    }

    fn is_fifo(&self) -> bool {
        self.0.is_fifo()
    }

    fn is_socket(&self) -> bool {
        self.0.is_socket()
    }
}