use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FileKind {
File,
Dir,
Symlink,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Metadata {
len: u64,
mode: u32,
kind: FileKind,
modified: SystemTime,
accessed: SystemTime,
created: SystemTime,
}
impl Metadata {
pub(crate) fn from_stat(raw: &libc::stat) -> Self {
Self {
len: raw.st_size.max(0) as u64,
mode: raw.st_mode as u32,
kind: FileKind::from_mode(raw.st_mode),
modified: stamp(raw.st_mtime, raw.st_mtime_nsec),
accessed: stamp(raw.st_atime, raw.st_atime_nsec),
created: stamp(raw.st_birthtime, raw.st_birthtime_nsec),
}
}
pub fn len(&self) -> u64 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn mode(&self) -> u32 {
self.mode
}
pub fn kind(&self) -> FileKind {
self.kind
}
pub fn is_file(&self) -> bool {
self.kind == FileKind::File
}
pub fn is_dir(&self) -> bool {
self.kind == FileKind::Dir
}
pub fn is_symlink(&self) -> bool {
self.kind == FileKind::Symlink
}
pub fn modified(&self) -> SystemTime {
self.modified
}
pub fn accessed(&self) -> SystemTime {
self.accessed
}
pub fn created(&self) -> SystemTime {
self.created
}
}
impl FileKind {
pub(crate) fn from_mode(mode: libc::mode_t) -> Self {
match mode & libc::S_IFMT {
libc::S_IFREG => Self::File,
libc::S_IFDIR => Self::Dir,
libc::S_IFLNK => Self::Symlink,
_ => Self::Other,
}
}
}
fn stamp(secs: libc::time_t, nanos: libc::c_long) -> SystemTime {
let nanos = nanos.clamp(0, 999_999_999) as u32;
if secs >= 0 {
return UNIX_EPOCH
.checked_add(Duration::new(secs as u64, nanos))
.unwrap_or(UNIX_EPOCH);
}
UNIX_EPOCH
.checked_sub(Duration::from_secs(secs.unsigned_abs()))
.and_then(|time| time.checked_add(Duration::from_nanos(nanos as u64)))
.unwrap_or(UNIX_EPOCH)
}