use std::{
io,
os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt},
time::SystemTime,
};
#[derive(Clone)]
pub(crate) struct Metadata {
stat: rustix::fs::Stat,
}
impl Metadata {
pub(crate) fn from_stat(stat: rustix::fs::Stat) -> Self {
Self { stat }
}
pub(crate) fn file_type(&self) -> FileType {
FileType::from_mode(self.stat.st_mode)
}
pub(crate) fn is_dir(&self) -> bool {
self.file_type().is_dir()
}
pub(crate) fn is_file(&self) -> bool {
self.file_type().is_file()
}
pub(crate) fn is_symlink(&self) -> bool {
self.file_type().is_symlink()
}
pub(crate) fn len(&self) -> u64 {
self.stat.st_size as u64
}
pub(crate) fn permissions(&self) -> Permissions {
Permissions::from_mode(self.stat.st_mode as u32)
}
pub(crate) fn modified(&self) -> io::Result<SystemTime> {
timespec(self.stat.st_mtime, stat_mtime_nsec(&self.stat))
}
pub(crate) fn accessed(&self) -> io::Result<SystemTime> {
timespec(self.stat.st_atime, stat_atime_nsec(&self.stat))
}
pub(crate) fn created(&self) -> io::Result<SystemTime> {
#[cfg(target_vendor = "apple")]
{
timespec(self.stat.st_birthtime, self.stat.st_birthtime_nsec)
}
#[cfg(not(target_vendor = "apple"))]
{
let _ = self;
Err(io::Error::new(
io::ErrorKind::Unsupported,
"creation time is not available on this platform currently",
))
}
}
}
impl MetadataExt for Metadata {
fn dev(&self) -> u64 {
self.stat.st_dev as u64
}
fn ino(&self) -> u64 {
self.stat.st_ino
}
fn mode(&self) -> u32 {
self.stat.st_mode as u32
}
fn nlink(&self) -> u64 {
self.stat.st_nlink as u64
}
fn uid(&self) -> u32 {
self.stat.st_uid
}
fn gid(&self) -> u32 {
self.stat.st_gid
}
fn rdev(&self) -> u64 {
self.stat.st_rdev as u64
}
fn size(&self) -> u64 {
self.stat.st_size as u64
}
fn atime(&self) -> i64 {
self.stat.st_atime
}
fn atime_nsec(&self) -> i64 {
stat_atime_nsec(&self.stat)
}
fn mtime(&self) -> i64 {
self.stat.st_mtime
}
fn mtime_nsec(&self) -> i64 {
stat_mtime_nsec(&self.stat)
}
fn ctime(&self) -> i64 {
self.stat.st_ctime
}
fn ctime_nsec(&self) -> i64 {
stat_ctime_nsec(&self.stat)
}
fn blksize(&self) -> u64 {
self.stat.st_blksize as u64
}
fn blocks(&self) -> u64 {
self.stat.st_blocks as u64
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct FileType {
mode: libc::mode_t,
}
impl FileType {
pub(crate) fn from_std(file_type: std::fs::FileType) -> Self {
let mode = if file_type.is_symlink() {
libc::S_IFLNK
} else if file_type.is_dir() {
libc::S_IFDIR
} else if file_type.is_file() {
libc::S_IFREG
} else if file_type.is_block_device() {
libc::S_IFBLK
} else if file_type.is_char_device() {
libc::S_IFCHR
} else if file_type.is_fifo() {
libc::S_IFIFO
} else if file_type.is_socket() {
libc::S_IFSOCK
} else {
0
};
Self::from_mode(mode as libc::mode_t)
}
fn from_mode(mode: libc::mode_t) -> Self {
Self { mode }
}
pub(crate) fn is_dir(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFDIR
}
pub(crate) fn is_file(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFREG
}
pub(crate) fn is_symlink(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFLNK
}
}
impl FileTypeExt for FileType {
fn is_block_device(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFBLK
}
fn is_char_device(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFCHR
}
fn is_fifo(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFIFO
}
fn is_socket(&self) -> bool {
self.mode & libc::S_IFMT == libc::S_IFSOCK
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct Permissions {
mode: libc::mode_t,
}
impl Permissions {
pub(crate) fn readonly(&self) -> bool {
self.mode & 0o222 == 0
}
pub(crate) fn set_readonly(&mut self, readonly: bool) {
if readonly {
self.mode &= !0o222;
} else {
self.mode |= 0o200;
}
}
}
impl PermissionsExt for Permissions {
fn mode(&self) -> u32 {
self.mode as u32
}
fn set_mode(&mut self, mode: u32) {
self.mode = mode as libc::mode_t;
}
fn from_mode(mode: u32) -> Self {
Self {
mode: mode as libc::mode_t,
}
}
}
fn stat_atime_nsec(stat: &rustix::fs::Stat) -> i64 {
stat.st_atime_nsec
}
fn stat_mtime_nsec(stat: &rustix::fs::Stat) -> i64 {
stat.st_mtime_nsec
}
fn stat_ctime_nsec(stat: &rustix::fs::Stat) -> i64 {
stat.st_ctime_nsec
}
fn timespec(secs: libc::time_t, nsecs: libc::c_long) -> io::Result<SystemTime> {
super::system_time_from_unix(secs, nsecs as u32)
}