mod unix;
mod windows;
use std::{os::unix::fs::MetadataExt, path::Path, time::SystemTime};
use super::{file_type::FileType, permissions::Permissions};
use crate::driver::op::Op;
pub async fn metadata<P: AsRef<Path>>(path: P) -> std::io::Result<Metadata> {
#[cfg(target_os = "linux")]
let flags = libc::AT_STATX_SYNC_AS_STAT;
#[cfg(target_os = "linux")]
let op = Op::statx_using_path(path, flags)?;
#[cfg(target_os = "macos")]
let op = Op::statx_using_path(path, true)?;
op.statx_result().await.map(FileAttr::from).map(Metadata)
}
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::io::Result<Metadata> {
#[cfg(target_os = "linux")]
let flags = libc::AT_STATX_SYNC_AS_STAT | libc::AT_SYMLINK_NOFOLLOW;
#[cfg(target_os = "linux")]
let op = Op::statx_using_path(path, flags)?;
#[cfg(target_os = "macos")]
let op = Op::statx_using_path(path, false)?;
op.statx_result().await.map(FileAttr::from).map(Metadata)
}
#[cfg(unix)]
pub(crate) use unix::FileAttr;
#[cfg(unix)]
pub struct Metadata(pub(crate) FileAttr);
impl Metadata {
pub fn is_dir(&self) -> bool {
self.0.stat.st_mode & libc::S_IFMT == libc::S_IFDIR
}
pub fn is_file(&self) -> bool {
self.0.stat.st_mode & libc::S_IFMT == libc::S_IFREG
}
pub fn is_symlink(&self) -> bool {
self.0.stat.st_mode & libc::S_IFMT == libc::S_IFLNK
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u64 {
self.0.size()
}
pub fn modified(&self) -> std::io::Result<SystemTime> {
let mtime = self.0.stat.st_mtime;
let mtime_nsec = self.0.stat.st_mtime_nsec as u32;
Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(mtime as u64, mtime_nsec))
}
pub fn accessed(&self) -> std::io::Result<SystemTime> {
let atime = self.0.stat.st_atime;
let atime_nsec = self.0.stat.st_atime_nsec as u32;
Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(atime as u64, atime_nsec))
}
#[cfg(target_os = "linux")]
pub fn created(&self) -> std::io::Result<SystemTime> {
if let Some(extra) = self.0.statx_extra_fields.as_ref() {
return if extra.stx_mask & libc::STATX_BTIME != 0 {
let btime = extra.stx_btime.tv_sec;
let btime_nsec = extra.stx_btime.tv_nsec;
Ok(SystemTime::UNIX_EPOCH + std::time::Duration::new(btime as u64, btime_nsec))
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Creation time is not available",
))
};
}
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Creation time is not available",
))
}
#[cfg(unix)]
pub fn permissions(&self) -> Permissions {
use super::permissions::Permissions;
Permissions(self.0.perm())
}
#[cfg(unix)]
pub fn file_type(&self) -> FileType {
self.0.file_type()
}
}
impl std::fmt::Debug for Metadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("Metadata");
debug.field("permissions", &self.permissions());
debug.field("len", &self.len());
if let Ok(modified) = self.modified() {
debug.field("modified", &modified);
}
if let Ok(accessed) = self.accessed() {
debug.field("accessed", &accessed);
}
#[cfg(target_os = "linux")]
if let Ok(created) = self.created() {
debug.field("created", &created);
}
debug.finish_non_exhaustive()
}
}
#[cfg(all(target_os = "linux", not(target_pointer_width = "32")))]
impl MetadataExt for Metadata {
fn dev(&self) -> u64 {
self.0.stat.st_dev
}
fn ino(&self) -> u64 {
self.0.stat.st_ino
}
fn mode(&self) -> u32 {
self.0.stat.st_mode
}
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
fn nlink(&self) -> u64 {
self.0.stat.st_nlink.into()
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
fn nlink(&self) -> u64 {
self.0.stat.st_nlink
}
fn uid(&self) -> u32 {
self.0.stat.st_uid
}
fn gid(&self) -> u32 {
self.0.stat.st_gid
}
fn rdev(&self) -> u64 {
self.0.stat.st_rdev
}
fn size(&self) -> u64 {
self.0.stat.st_size as u64
}
fn atime(&self) -> i64 {
self.0.stat.st_atime
}
fn atime_nsec(&self) -> i64 {
self.0.stat.st_atime_nsec
}
fn mtime(&self) -> i64 {
self.0.stat.st_mtime
}
fn mtime_nsec(&self) -> i64 {
self.0.stat.st_mtime_nsec
}
fn ctime(&self) -> i64 {
self.0.stat.st_ctime
}
fn ctime_nsec(&self) -> i64 {
self.0.stat.st_ctime_nsec
}
fn blksize(&self) -> u64 {
self.0.stat.st_blksize as u64
}
fn blocks(&self) -> u64 {
self.0.stat.st_blocks as u64
}
}
#[cfg(all(target_os = "macos", not(target_pointer_width = "32")))]
impl MetadataExt for Metadata {
fn dev(&self) -> u64 {
self.0.stat.st_dev as u64
}
fn ino(&self) -> u64 {
self.0.stat.st_ino
}
fn mode(&self) -> u32 {
self.0.stat.st_mode as u32
}
fn nlink(&self) -> u64 {
self.0.stat.st_nlink.into()
}
fn uid(&self) -> u32 {
self.0.stat.st_uid
}
fn gid(&self) -> u32 {
self.0.stat.st_gid
}
fn rdev(&self) -> u64 {
self.0.stat.st_rdev as u64
}
fn size(&self) -> u64 {
self.0.stat.st_size as u64
}
fn atime(&self) -> i64 {
self.0.stat.st_atime
}
fn atime_nsec(&self) -> i64 {
self.0.stat.st_atime_nsec
}
fn mtime(&self) -> i64 {
self.0.stat.st_mtime
}
fn mtime_nsec(&self) -> i64 {
self.0.stat.st_mtime_nsec
}
fn ctime(&self) -> i64 {
self.0.stat.st_ctime
}
fn ctime_nsec(&self) -> i64 {
self.0.stat.st_ctime_nsec
}
fn blksize(&self) -> u64 {
self.0.stat.st_blksize as u64
}
fn blocks(&self) -> u64 {
self.0.stat.st_blocks as u64
}
}
#[cfg(all(unix, target_pointer_width = "32"))]
impl MetadataExt for Metadata {
fn dev(&self) -> u64 {
self.0.stat.st_dev.into()
}
fn ino(&self) -> u64 {
self.0.stat.st_ino.into()
}
fn mode(&self) -> u32 {
self.0.stat.st_mode
}
fn nlink(&self) -> u64 {
self.0.stat.st_nlink.into()
}
fn uid(&self) -> u32 {
self.0.stat.st_uid
}
fn gid(&self) -> u32 {
self.0.stat.st_gid
}
fn rdev(&self) -> u64 {
self.0.stat.st_rdev.into()
}
fn size(&self) -> u64 {
self.0.stat.st_size as u64
}
fn atime(&self) -> i64 {
self.0.stat.st_atime.into()
}
fn atime_nsec(&self) -> i64 {
self.0.stat.st_atime_nsec.into()
}
fn mtime(&self) -> i64 {
self.0.stat.st_mtime.into()
}
fn mtime_nsec(&self) -> i64 {
self.0.stat.st_mtime_nsec.into()
}
fn ctime(&self) -> i64 {
self.0.stat.st_ctime.into()
}
fn ctime_nsec(&self) -> i64 {
self.0.stat.st_ctime_nsec.into()
}
fn blksize(&self) -> u64 {
self.0.stat.st_blksize as u64
}
fn blocks(&self) -> u64 {
self.0.stat.st_blocks as u64
}
}