diff --git a/src/fs/fields.rs b/src/fs/fields.rs index d64b59a..3249e9a 100644 --- a/src/fs/fields.rs +++ b/src/fs/fields.rs @@ -82,6 +82,17 @@ pub struct Permissions { pub setuid: bool, } +/// The file's FileAttributes field, available only on Windows. +#[derive(Copy, Clone)] +pub struct Attributes { + pub archive: bool, + pub directory: bool, + pub readonly: bool, + pub hidden: bool, + pub system: bool, + pub reparse_point: bool, +} + /// The three pieces of information that are displayed as a single column in /// the details view. These values are fused together to make the output a /// little more compressed. @@ -90,6 +101,8 @@ pub struct PermissionsPlus { pub file_type: Type, #[cfg(unix)] pub permissions: Permissions, + #[cfg(windows)] + pub attributes: Attributes, pub xattrs: bool, } diff --git a/src/fs/file.rs b/src/fs/file.rs index 94c708a..b714659 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -3,6 +3,8 @@ 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}; @@ -463,6 +465,21 @@ 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; + + 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. diff --git a/src/output/render/permissions.rs b/src/output/render/permissions.rs index f8ce15c..a2f83a5 100644 --- a/src/output/render/permissions.rs +++ b/src/output/render/permissions.rs @@ -6,9 +6,9 @@ use crate::output::render::FiletypeColours; impl f::PermissionsPlus { + #[cfg(unix)] pub fn render(&self, colours: &C) -> TextCell { let mut chars = vec![ self.file_type.render(colours) ]; - #[cfg(unix)] chars.extend(self.permissions.render(colours, self.file_type.is_regular_file())); if self.xattrs { @@ -23,6 +23,17 @@ impl f::PermissionsPlus { contents: chars.into(), } } + + #[cfg(windows)] + pub fn render(&self, colours: &C) -> TextCell { + let mut chars = vec![ self.attributes.render_type(colours) ]; + chars.extend(self.attributes.render(colours)); + + TextCell { + width: DisplayWidth::from(chars.len()), + contents: chars.into(), + } + } } @@ -77,6 +88,33 @@ impl f::Permissions { } } +impl f::Attributes { + pub fn render(&self, colours: &C) -> Vec> { + let bit = |bit, chr: &'static str, style: Style| { + if bit { style.paint(chr) } + else { colours.dash().paint("-") } + }; + + vec![ + bit(self.archive, "a", colours.normal()), + bit(self.readonly, "r", colours.user_read()), + bit(self.hidden, "h", colours.special_user_file()), + bit(self.system, "s", colours.special_other()), + ] + } + + pub fn render_type(&self, colours: &C) -> ANSIString<'static> { + if self.reparse_point { + return colours.pipe().paint("l") + } + else if self.directory { + return colours.directory().paint("d") + } + else { + return colours.dash().paint("-") + } + } +} pub trait Colours { fn dash(&self) -> Style; diff --git a/src/output/table.rs b/src/output/table.rs index af1061c..7c254cb 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -171,7 +171,10 @@ impl Column { /// to have a header row printed. pub fn header(self) -> &'static str { match self { + #[cfg(unix)] Self::Permissions => "Permissions", + #[cfg(windows)] + Self::Permissions => "Mode", Self::FileSize => "Size", Self::Timestamp(t) => t.header(), #[cfg(unix)] @@ -422,6 +425,8 @@ impl<'a, 'f> Table<'a> { file_type: file.type_char(), #[cfg(unix)] permissions: file.permissions(), + #[cfg(windows)] + attributes: file.attributes(), xattrs, } }