use crate::fs::{FileType, ImplFileTypeExt, ImplMetadataExt, Permissions}; use crate::time::SystemTime; use std::{fs, io}; /// Metadata information about a file. /// /// This corresponds to [`std::fs::Metadata`]. /// ///
/// We need to define our own version because the libstd `Metadata` doesn't /// have a public constructor that we can use. ///
#[derive(Debug, Clone)] pub struct Metadata { pub(crate) file_type: FileType, pub(crate) len: u64, pub(crate) permissions: Permissions, pub(crate) modified: Option, pub(crate) accessed: Option, pub(crate) created: Option, pub(crate) ext: ImplMetadataExt, } #[allow(clippy::len_without_is_empty)] impl Metadata { /// Constructs a new instance of `Self` from the given [`std::fs::File`]. #[inline] pub fn from_file(file: &fs::File) -> io::Result { let std = file.metadata()?; let ext = ImplMetadataExt::from(file, &std)?; let file_type = ImplFileTypeExt::from(file, &std)?; Ok(Self::from_parts(std, ext, file_type)) } /// Constructs a new instance of `Self` from the given /// [`std::fs::Metadata`]. /// /// As with the comments in [`std::fs::Metadata::volume_serial_number`] and /// nearby functions, some fields of the resulting metadata will be `None`. /// /// [`std::fs::Metadata::volume_serial_number`]: https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html#tymethod.volume_serial_number #[inline] pub fn from_just_metadata(std: fs::Metadata) -> Self { let ext = ImplMetadataExt::from_just_metadata(&std); let file_type = ImplFileTypeExt::from_just_metadata(&std); Self::from_parts(std, ext, file_type) } #[inline] fn from_parts(std: fs::Metadata, ext: ImplMetadataExt, file_type: FileType) -> Self { Self { file_type, len: std.len(), permissions: Permissions::from_std(std.permissions()), modified: std.modified().ok().map(SystemTime::from_std), accessed: std.accessed().ok().map(SystemTime::from_std), created: std.created().ok().map(SystemTime::from_std), ext, } } /// Returns the file type for this metadata. /// /// This corresponds to [`std::fs::Metadata::file_type`]. #[inline] pub const fn file_type(&self) -> FileType { self.file_type } /// Returns `true` if this metadata is for a directory. /// /// This corresponds to [`std::fs::Metadata::is_dir`]. #[inline] pub fn is_dir(&self) -> bool { self.file_type.is_dir() } /// Returns `true` if this metadata is for a regular file. /// /// This corresponds to [`std::fs::Metadata::is_file`]. #[inline] pub fn is_file(&self) -> bool { self.file_type.is_file() } /// Returns `true` if this metadata is for a symbolic link. /// /// This corresponds to [`std::fs::Metadata::is_symlink`]. #[inline] pub fn is_symlink(&self) -> bool { self.file_type.is_symlink() } /// Returns the size of the file, in bytes, this metadata is for. /// /// This corresponds to [`std::fs::Metadata::len`]. #[inline] pub const fn len(&self) -> u64 { self.len } /// Returns the permissions of the file this metadata is for. /// /// This corresponds to [`std::fs::Metadata::permissions`]. #[inline] pub fn permissions(&self) -> Permissions { self.permissions.clone() } /// Returns the last modification time listed in this metadata. /// /// This corresponds to [`std::fs::Metadata::modified`]. #[inline] pub fn modified(&self) -> io::Result { #[cfg(io_error_uncategorized)] { self.modified.ok_or_else(|| { io::Error::new( io::ErrorKind::Unsupported, "modified time metadata not available on this platform", ) }) } #[cfg(not(io_error_uncategorized))] { self.modified.ok_or_else(|| { io::Error::new( io::ErrorKind::Other, "modified time metadata not available on this platform", ) }) } } /// Returns the last access time of this metadata. /// /// This corresponds to [`std::fs::Metadata::accessed`]. #[inline] pub fn accessed(&self) -> io::Result { #[cfg(io_error_uncategorized)] { self.accessed.ok_or_else(|| { io::Error::new( io::ErrorKind::Unsupported, "accessed time metadata not available on this platform", ) }) } #[cfg(not(io_error_uncategorized))] { self.accessed.ok_or_else(|| { io::Error::new( io::ErrorKind::Other, "accessed time metadata not available on this platform", ) }) } } /// Returns the creation time listed in this metadata. /// /// This corresponds to [`std::fs::Metadata::created`]. #[inline] pub fn created(&self) -> io::Result { #[cfg(io_error_uncategorized)] { self.created.ok_or_else(|| { io::Error::new( io::ErrorKind::Unsupported, "created time metadata not available on this platform", ) }) } #[cfg(not(io_error_uncategorized))] { self.created.ok_or_else(|| { io::Error::new( io::ErrorKind::Other, "created time metadata not available on this platform", ) }) } } /// Determine if `self` and `other` refer to the same inode on the same /// device. #[cfg(any(not(windows), windows_by_handle))] pub(crate) fn is_same_file(&self, other: &Self) -> bool { self.ext.is_same_file(&other.ext) } /// `MetadataExt` requires nightly to be implemented, but we sometimes /// just need the file attributes. #[cfg(windows)] #[inline] pub(crate) fn file_attributes(&self) -> u32 { self.ext.file_attributes() } } /// Unix-specific extensions for [`MetadataExt`]. /// /// This corresponds to [`std::os::unix::fs::MetadataExt`]. #[cfg(any(unix, target_os = "vxworks"))] pub trait MetadataExt { /// Returns the ID of the device containing the file. fn dev(&self) -> u64; /// Returns the inode number. fn ino(&self) -> u64; /// Returns the rights applied to this file. fn mode(&self) -> u32; /// Returns the number of hard links pointing to this file. fn nlink(&self) -> u64; /// Returns the user ID of the owner of this file. fn uid(&self) -> u32; /// Returns the group ID of the owner of this file. fn gid(&self) -> u32; /// Returns the device ID of this file (if it is a special one). fn rdev(&self) -> u64; /// Returns the total size of this file in bytes. fn size(&self) -> u64; /// Returns the last access time of the file, in seconds since Unix Epoch. fn atime(&self) -> i64; /// Returns the last access time of the file, in nanoseconds since [`atime`]. fn atime_nsec(&self) -> i64; /// Returns the last modification time of the file, in seconds since Unix Epoch. fn mtime(&self) -> i64; /// Returns the last modification time of the file, in nanoseconds since [`mtime`]. fn mtime_nsec(&self) -> i64; /// Returns the last status change time of the file, in seconds since Unix Epoch. fn ctime(&self) -> i64; /// Returns the last status change time of the file, in nanoseconds since [`ctime`]. fn ctime_nsec(&self) -> i64; /// Returns the block size for filesystem I/O. fn blksize(&self) -> u64; /// Returns the number of blocks allocated to the file, in 512-byte units. fn blocks(&self) -> u64; #[cfg(target_os = "vxworks")] fn attrib(&self) -> u8; } /// WASI-specific extensions for [`MetadataExt`]. /// /// This corresponds to [`std::os::wasi::fs::MetadataExt`]. #[cfg(target_os = "wasi")] pub trait MetadataExt { /// Returns the ID of the device containing the file. fn dev(&self) -> u64; /// Returns the inode number. fn ino(&self) -> u64; /// Returns the number of hard links pointing to this file. fn nlink(&self) -> u64; /// Returns the total size of this file in bytes. fn size(&self) -> u64; /// Returns the last access time of the file, in seconds since Unix Epoch. fn atim(&self) -> u64; /// Returns the last modification time of the file, in seconds since Unix Epoch. fn mtim(&self) -> u64; /// Returns the last status change time of the file, in seconds since Unix Epoch. fn ctim(&self) -> u64; } /// Windows-specific extensions to [`Metadata`]. /// /// This corresponds to [`std::os::windows::fs::MetadataExt`]. #[cfg(windows)] pub trait MetadataExt { /// Returns the value of the `dwFileAttributes` field of this metadata. fn file_attributes(&self) -> u32; /// Returns the value of the `ftCreationTime` field of this metadata. fn creation_time(&self) -> u64; /// Returns the value of the `ftLastAccessTime` field of this metadata. fn last_access_time(&self) -> u64; /// Returns the value of the `ftLastWriteTime` field of this metadata. fn last_write_time(&self) -> u64; /// Returns the value of the `nFileSize{High,Low}` fields of this metadata. fn file_size(&self) -> u64; /// Returns the value of the `dwVolumeSerialNumber` field of this metadata. #[cfg(windows_by_handle)] fn volume_serial_number(&self) -> Option; /// Returns the value of the `nNumberOfLinks` field of this metadata. #[cfg(windows_by_handle)] fn number_of_links(&self) -> Option; /// Returns the value of the `nFileIndex{Low,High}` fields of this metadata. #[cfg(windows_by_handle)] fn file_index(&self) -> Option; } #[cfg(unix)] impl MetadataExt for Metadata { #[inline] fn dev(&self) -> u64 { crate::fs::MetadataExt::dev(&self.ext) } #[inline] fn ino(&self) -> u64 { crate::fs::MetadataExt::ino(&self.ext) } #[inline] fn mode(&self) -> u32 { crate::fs::MetadataExt::mode(&self.ext) } #[inline] fn nlink(&self) -> u64 { crate::fs::MetadataExt::nlink(&self.ext) } #[inline] fn uid(&self) -> u32 { crate::fs::MetadataExt::uid(&self.ext) } #[inline] fn gid(&self) -> u32 { crate::fs::MetadataExt::gid(&self.ext) } #[inline] fn rdev(&self) -> u64 { crate::fs::MetadataExt::rdev(&self.ext) } #[inline] fn size(&self) -> u64 { crate::fs::MetadataExt::size(&self.ext) } #[inline] fn atime(&self) -> i64 { crate::fs::MetadataExt::atime(&self.ext) } #[inline] fn atime_nsec(&self) -> i64 { crate::fs::MetadataExt::atime_nsec(&self.ext) } #[inline] fn mtime(&self) -> i64 { crate::fs::MetadataExt::mtime(&self.ext) } #[inline] fn mtime_nsec(&self) -> i64 { crate::fs::MetadataExt::mtime_nsec(&self.ext) } #[inline] fn ctime(&self) -> i64 { crate::fs::MetadataExt::ctime(&self.ext) } #[inline] fn ctime_nsec(&self) -> i64 { crate::fs::MetadataExt::ctime_nsec(&self.ext) } #[inline] fn blksize(&self) -> u64 { crate::fs::MetadataExt::blksize(&self.ext) } #[inline] fn blocks(&self) -> u64 { crate::fs::MetadataExt::blocks(&self.ext) } } #[cfg(target_os = "wasi")] impl MetadataExt for Metadata { #[inline] fn dev(&self) -> u64 { crate::fs::MetadataExt::dev(&self.ext) } #[inline] fn ino(&self) -> u64 { crate::fs::MetadataExt::ino(&self.ext) } #[inline] fn nlink(&self) -> u64 { crate::fs::MetadataExt::nlink(&self.ext) } #[inline] fn size(&self) -> u64 { crate::fs::MetadataExt::size(&self.ext) } #[inline] fn atim(&self) -> u64 { crate::fs::MetadataExt::atim(&self.ext) } #[inline] fn mtim(&self) -> u64 { crate::fs::MetadataExt::mtim(&self.ext) } #[inline] fn ctim(&self) -> u64 { crate::fs::MetadataExt::ctim(&self.ext) } } #[cfg(target_os = "vxworks")] impl MetadataExt for Metadata { #[inline] fn dev(&self) -> u64 { self.ext.dev() } #[inline] fn ino(&self) -> u64 { self.ext.ino() } #[inline] fn mode(&self) -> u32 { self.ext.mode() } #[inline] fn nlink(&self) -> u64 { self.ext.nlink() } #[inline] fn uid(&self) -> u32 { self.ext.uid() } #[inline] fn gid(&self) -> u32 { self.ext.gid() } #[inline] fn rdev(&self) -> u64 { self.ext.rdev() } #[inline] fn size(&self) -> u64 { self.ext.size() } #[inline] fn atime(&self) -> i64 { self.ext.atime() } #[inline] fn atime_nsec(&self) -> i64 { self.ext.atime_nsec() } #[inline] fn mtime(&self) -> i64 { self.ext.mtime() } #[inline] fn mtime_nsec(&self) -> i64 { self.ext.mtime_nsec() } #[inline] fn ctime(&self) -> i64 { self.ext.ctime() } #[inline] fn ctime_nsec(&self) -> i64 { self.ext.ctime_nsec() } #[inline] fn blksize(&self) -> u64 { self.ext.blksize() } #[inline] fn blocks(&self) -> u64 { self.ext.blocks() } } #[cfg(windows)] impl MetadataExt for Metadata { #[inline] fn file_attributes(&self) -> u32 { self.ext.file_attributes() } #[inline] fn creation_time(&self) -> u64 { self.ext.creation_time() } #[inline] fn last_access_time(&self) -> u64 { self.ext.last_access_time() } #[inline] fn last_write_time(&self) -> u64 { self.ext.last_write_time() } #[inline] fn file_size(&self) -> u64 { self.ext.file_size() } #[inline] #[cfg(windows_by_handle)] fn volume_serial_number(&self) -> Option { self.ext.volume_serial_number() } #[inline] #[cfg(windows_by_handle)] fn number_of_links(&self) -> Option { self.ext.number_of_links() } #[inline] #[cfg(windows_by_handle)] fn file_index(&self) -> Option { self.ext.file_index() } } /// Extension trait to allow `volume_serial_number` etc. to be exposed by /// the `cap-fs-ext` crate. /// /// This is hidden from the main API since this functionality isn't present in /// `std`. Use `cap_fs_ext::MetadataExt` instead of calling this directly. #[cfg(windows)] #[doc(hidden)] pub trait _WindowsByHandle { fn file_attributes(&self) -> u32; fn volume_serial_number(&self) -> Option; fn number_of_links(&self) -> Option; fn file_index(&self) -> Option; }