|
|
@@ -1,7 +1,10 @@
|
|
|
//! Files, and methods and fields to access their metadata.
|
|
|
|
|
|
use std::io;
|
|
|
+#[cfg(unix)]
|
|
|
use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
|
|
|
+#[cfg(windows)]
|
|
|
+use std::os::windows::fs::MetadataExt;
|
|
|
use std::path::{Path, PathBuf};
|
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
|
|
|
|
@@ -174,6 +177,7 @@ impl<'dir> File<'dir> {
|
|
|
/// Whether this file is both a regular file *and* executable for the
|
|
|
/// current user. An executable file has a different purpose from an
|
|
|
/// executable directory, so they should be highlighted differently.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn is_executable_file(&self) -> bool {
|
|
|
let bit = modes::USER_EXECUTE;
|
|
|
self.is_file() && (self.metadata.permissions().mode() & bit) == bit
|
|
|
@@ -185,21 +189,25 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
|
|
|
/// Whether this file is a named pipe on the filesystem.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn is_pipe(&self) -> bool {
|
|
|
self.metadata.file_type().is_fifo()
|
|
|
}
|
|
|
|
|
|
/// Whether this file is a char device on the filesystem.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn is_char_device(&self) -> bool {
|
|
|
self.metadata.file_type().is_char_device()
|
|
|
}
|
|
|
|
|
|
/// Whether this file is a block device on the filesystem.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn is_block_device(&self) -> bool {
|
|
|
self.metadata.file_type().is_block_device()
|
|
|
}
|
|
|
|
|
|
/// Whether this file is a socket on the filesystem.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn is_socket(&self) -> bool {
|
|
|
self.metadata.file_type().is_socket()
|
|
|
}
|
|
|
@@ -270,6 +278,7 @@ impl<'dir> File<'dir> {
|
|
|
/// is uncommon, while you come across directories and other types
|
|
|
/// with multiple links much more often. Thus, it should get highlighted
|
|
|
/// more attentively.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn links(&self) -> f::Links {
|
|
|
let count = self.metadata.nlink();
|
|
|
|
|
|
@@ -280,6 +289,7 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
|
|
|
/// This file’s inode.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn inode(&self) -> f::Inode {
|
|
|
f::Inode(self.metadata.ino())
|
|
|
}
|
|
|
@@ -287,6 +297,7 @@ impl<'dir> File<'dir> {
|
|
|
/// This file’s number of filesystem blocks.
|
|
|
///
|
|
|
/// (Not the size of each block, which we don’t actually report on)
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn blocks(&self) -> f::Blocks {
|
|
|
if self.is_file() || self.is_link() {
|
|
|
f::Blocks::Some(self.metadata.blocks())
|
|
|
@@ -297,11 +308,13 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
|
|
|
/// The ID of the user that own this file.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn user(&self) -> f::User {
|
|
|
f::User(self.metadata.uid())
|
|
|
}
|
|
|
|
|
|
/// The ID of the group that owns this file.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn group(&self) -> f::Group {
|
|
|
f::Group(self.metadata.gid())
|
|
|
}
|
|
|
@@ -314,6 +327,7 @@ impl<'dir> File<'dir> {
|
|
|
///
|
|
|
/// Block and character devices return their device IDs, because they
|
|
|
/// usually just have a file size of zero.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn size(&self) -> f::Size {
|
|
|
if self.is_directory() {
|
|
|
f::Size::None
|
|
|
@@ -335,12 +349,23 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #[cfg(windows)]
|
|
|
+ pub fn size(&self) -> f::Size {
|
|
|
+ if self.is_directory() {
|
|
|
+ f::Size::None
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ f::Size::Some(self.metadata.len())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// This file’s last modified timestamp, if available on this platform.
|
|
|
pub fn modified_time(&self) -> Option<SystemTime> {
|
|
|
self.metadata.modified().ok()
|
|
|
}
|
|
|
|
|
|
/// This file’s last changed timestamp, if available on this platform.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn changed_time(&self) -> Option<SystemTime> {
|
|
|
let (mut sec, mut nanosec) = (self.metadata.ctime(), self.metadata.ctime_nsec());
|
|
|
|
|
|
@@ -359,6 +384,11 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #[cfg(windows)]
|
|
|
+ pub fn changed_time(&self) -> Option<SystemTime> {
|
|
|
+ return self.modified_time()
|
|
|
+ }
|
|
|
+
|
|
|
/// This file’s last accessed timestamp, if available on this platform.
|
|
|
pub fn accessed_time(&self) -> Option<SystemTime> {
|
|
|
self.metadata.accessed().ok()
|
|
|
@@ -374,6 +404,7 @@ impl<'dir> File<'dir> {
|
|
|
/// This is used a the leftmost character of the permissions column.
|
|
|
/// The file type can usually be guessed from the colour of the file, but
|
|
|
/// ls puts this character there.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn type_char(&self) -> f::Type {
|
|
|
if self.is_file() {
|
|
|
f::Type::File
|
|
|
@@ -401,7 +432,21 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #[cfg(windows)]
|
|
|
+ pub fn type_char(&self) -> f::Type {
|
|
|
+ if self.is_file() {
|
|
|
+ f::Type::File
|
|
|
+ }
|
|
|
+ else if self.is_directory() {
|
|
|
+ f::Type::Directory
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ f::Type::Special
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// This file’s permissions, with flags for each bit.
|
|
|
+ #[cfg(unix)]
|
|
|
pub fn permissions(&self) -> f::Permissions {
|
|
|
let bits = self.metadata.mode();
|
|
|
let has_bit = |bit| bits & bit == bit;
|
|
|
@@ -425,6 +470,22 @@ impl<'dir> File<'dir> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #[cfg(windows)]
|
|
|
+ pub fn attributes(&self) -> f::Attributes {
|
|
|
+ let bits = self.metadata.file_attributes();
|
|
|
+ let has_bit = |bit| bits & bit == bit;
|
|
|
+
|
|
|
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
|
|
+ f::Attributes {
|
|
|
+ directory: has_bit(0x10),
|
|
|
+ archive: has_bit(0x20),
|
|
|
+ readonly: has_bit(0x1),
|
|
|
+ hidden: has_bit(0x2),
|
|
|
+ system: has_bit(0x4),
|
|
|
+ reparse_point: has_bit(0x400),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// Whether this file’s extension is any of the strings that get passed in.
|
|
|
///
|
|
|
/// This will always return `false` if the file has no extension.
|
|
|
@@ -482,6 +543,7 @@ impl<'dir> FileTarget<'dir> {
|
|
|
|
|
|
/// More readable aliases for the permission bits exposed by libc.
|
|
|
#[allow(trivial_numeric_casts)]
|
|
|
+#[cfg(unix)]
|
|
|
mod modes {
|
|
|
|
|
|
// The `libc::mode_t` type’s actual type varies, but the value returned
|
|
|
@@ -559,6 +621,7 @@ mod filename_test {
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
+ #[cfg(unix)]
|
|
|
fn topmost() {
|
|
|
assert_eq!("/", File::filename(Path::new("/")))
|
|
|
}
|