summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDom Slee <domslee1@gmail.com>2023-09-24 23:14:54 +1000
committerWei Zhang <kweizh@gmail.com>2024-02-16 11:47:12 +0800
commit92d4e9662200b87bf54ddf97c37cf13d5f2ec9c4 (patch)
tree268632e6eed55e9e3a07c228948de96f0b780c58
parent48b5adf9298338aaa67a1cf7a0c0ef00901388b0 (diff)
Implementation
-rw-r--r--src/app.rs4
-rw-r--r--src/color.rs17
-rw-r--r--src/config_file.rs7
-rw-r--r--src/core.rs24
-rw-r--r--src/display.rs30
-rw-r--r--src/flags/permission.rs23
-rw-r--r--src/icon.rs24
-rw-r--r--src/meta/filetype.rs13
-rw-r--r--src/meta/mod.rs98
-rw-r--r--src/meta/name.rs7
-rw-r--r--src/meta/permissions.rs2
-rw-r--r--src/meta/permissions_or_attributes.rs27
-rw-r--r--src/meta/windows_attributes.rs126
-rw-r--r--src/sort.rs74
-rw-r--r--src/theme/color.rs27
15 files changed, 386 insertions, 117 deletions
diff --git a/src/app.rs b/src/app.rs
index 6ef636b..86aec62 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -68,8 +68,8 @@ pub struct Cli {
#[arg(short, long, conflicts_with_all = ["depth", "recursive"])]
pub directory_only: bool,
- /// How to display permissions [default: rwx]
- #[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "disable"])]
+ /// How to display permissions [default: rwx for non-windows, attributes for windows]
+ #[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "attributes", "disable"])]
pub permission: Option<String>,
/// How to display size [default: default]
diff --git a/src/color.rs b/src/color.rs
index 8cb93c1..9a3a14b 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -38,6 +38,12 @@ pub enum Elem {
Acl,
Context,
+ /// Attributes
+ Archive,
+ AttributeRead,
+ Hidden,
+ System,
+
/// Last Time Modified
DayOld,
HourOld,
@@ -112,6 +118,11 @@ impl Elem {
Elem::Acl => theme.permission.acl,
Elem::Context => theme.permission.context,
+ Elem::Archive => theme.attributes.archive,
+ Elem::AttributeRead => theme.attributes.read,
+ Elem::Hidden => theme.attributes.hidden,
+ Elem::System => theme.attributes.system,
+
Elem::DayOld => theme.date.day_old,
Elem::HourOld => theme.date.hour_old,
Elem::Older => theme.date.older,
@@ -399,6 +410,12 @@ mod elem {
acl: Color::DarkCyan,
context: Color::Cyan,
},
+ attributes: color::Attributes {
+ read: Color::Green,
+ archive: Color::Yellow,
+ hidden: Color::Red,
+ system: Color::Magenta,
+ },
file_type: color::FileType {
file: color::File {
exec_uid: Color::AnsiValue(40), // Green3
diff --git a/src/config_file.rs b/src/config_file.rs
index b69c040..9d35873 100644
--- a/src/config_file.rs
+++ b/src/config_file.rs
@@ -299,8 +299,8 @@ size: default
# == Permission ==
# Specify the format of the permission column.
-# Possible value: rwx, octal
-permission: rwx
+# Possible value: rwx, octal, attributes, disable
+# permission: rwx
# == Sorting ==
sorting:
@@ -363,7 +363,6 @@ mod tests {
use crate::flags::color::{ColorOption, ThemeOption};
use crate::flags::icons::{IconOption, IconTheme};
use crate::flags::layout::Layout;
- use crate::flags::permission::PermissionFlag;
use crate::flags::size::SizeFlag;
use crate::flags::sorting::{DirGrouping, SortColumn};
use crate::flags::HyperlinkOption;
@@ -402,7 +401,7 @@ mod tests {
depth: None,
}),
size: Some(SizeFlag::Default),
- permission: Some(PermissionFlag::Rwx),
+ permission: None,
sorting: Some(config_file::Sorting {
column: Some(SortColumn::Name),
reverse: Some(false),
diff --git a/src/core.rs b/src/core.rs
index 54ec086..d806ada 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -1,8 +1,7 @@
use crate::color::Colors;
use crate::display;
use crate::flags::{
- ColorOption, Display, Flags, HyperlinkOption, Layout, Literal, PermissionFlag, SortOrder,
- ThemeOption,
+ ColorOption, Display, Flags, HyperlinkOption, Layout, Literal, SortOrder, ThemeOption,
};
use crate::git::GitCache;
use crate::icon::Icons;
@@ -106,18 +105,15 @@ impl Core {
};
for path in paths {
- let mut meta = match Meta::from_path(
- &path,
- self.flags.dereference.0,
- self.flags.permission == PermissionFlag::Disable,
- ) {
- Ok(meta) => meta,
- Err(err) => {
- print_error!("{}: {}.", path.display(), err);
- exit_code.set_if_greater(ExitCode::MajorIssue);
- continue;
- }
- };
+ let mut meta =
+ match Meta::from_path(&path, self.flags.dereference.0, self.flags.permission) {
+ Ok(meta) => meta,
+ Err(err) => {
+ print_error!("{}: {}.", path.display(), err);
+ exit_code.set_if_greater(ExitCode::MajorIssue);
+ continue;
+ }
+ };
let cache = if self.flags.blocks.0.contains(&Block::GitStatus) {
Some(GitCache::new(&path))
diff --git a/src/display.rs b/src/display.rs
index a6d4942..da4c897 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -347,8 +347,10 @@ fn get_output(
Block::Permission => {
block_vec.extend([
meta.file_type.render(colors),
- match meta.permissions {
- Some(permissions) => permissions.render(colors, flags),
+ match &meta.permissions_or_attributes {
+ Some(permissions_or_attributes) => {
+ permissions_or_attributes.render(colors, flags)
+ }
None => colorize_missing("?????????"),
},
match &meta.access_control {
@@ -491,7 +493,7 @@ mod tests {
use crate::app::Cli;
use crate::color;
use crate::color::Colors;
- use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme};
+ use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme, PermissionFlag};
use crate::icon::Icons;
use crate::meta::{FileType, Name};
use crate::Config;
@@ -686,7 +688,7 @@ mod tests {
dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().unwrap();
dir.child("one.d/.hidden").touch().unwrap();
- let mut metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let mut metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
@@ -719,7 +721,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().unwrap();
- let metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
@@ -760,7 +762,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().unwrap();
- let metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
@@ -800,7 +802,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap();
dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().unwrap();
- let metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(42, &flags, None)
.unwrap()
@@ -831,7 +833,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().unwrap();
dir.child("test").touch().unwrap();
- let metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(1, &flags, None)
.unwrap()
@@ -865,7 +867,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().unwrap();
- let metas = Meta::from_path(Path::new(dir.path()), false, false)
+ let metas = Meta::from_path(Path::new(dir.path()), false, PermissionFlag::Rwx)
.unwrap()
.recurse_into(1, &flags, None)
.unwrap()
@@ -895,11 +897,11 @@ mod tests {
let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the file");
- let file = Meta::from_path(&file_path, false, false).unwrap();
+ let file = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the dir");
- let dir = Meta::from_path(&dir_path, false, false).unwrap();
+ let dir = Meta::from_path(&dir_path, false, PermissionFlag::Rwx).unwrap();
assert_eq!(
display_folder_path(&dir),
@@ -945,15 +947,15 @@ mod tests {
let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the file");
- let file = Meta::from_path(&file_path, false, false).unwrap();
+ let file = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the dir");
- let dir = Meta::from_path(&dir_path, false, false).unwrap();
+ let dir = Meta::from_path(&dir_path, false, PermissionFlag::Rwx).unwrap();
let link_path = tmp_dir.path().join("link");
std::os::unix::fs::symlink("dir", &link_path).unwrap();
- let link = Meta::from_path(&link_path, false, false).unwrap();
+ let link = Meta::from_path(&link_path, false, PermissionFlag::Rwx).unwrap();
let grid_flags = Flags {
layout: Layout::Grid,
diff --git a/src/flags/permission.rs b/src/flags/permission.rs
index 2cced0b..bbaa562 100644
--- a/src/flags/permission.rs
+++ b/src/flags/permission.rs
@@ -13,10 +13,13 @@ use serde::Deserialize;
#[serde(rename_all = "kebab-case")]
pub enum PermissionFlag {
/// The variant to show file permissions in rwx format
- #[default]
+ #[cfg_attr(not(target_os = "windows"), default)]
Rwx,
/// The variant to show file permissions in octal format
Octal,
+ /// (windows only): Attributes from powershell's `Get-ChildItem`
+ #[cfg_attr(target_os = "windows", default)]
+ Attributes,
/// Disable the display of owner and permissions, may be used to speed up in Windows
Disable,
}
@@ -26,6 +29,7 @@ impl PermissionFlag {
match value {
"rwx" => Self::Rwx,
"octal" => Self::Octal,
+ "attributes" => Self::Attributes,
"disable" => Self::Disable,
// Invalid value should be handled by `clap` when building an `Cli`
other => unreachable!("Invalid value '{other}' for 'permission'"),
@@ -75,7 +79,12 @@ mod test {
#[test]
fn test_default() {
- assert_eq!(PermissionFlag::Rwx, PermissionFlag::default());
+ let expected = if cfg!(target_os = "windows") {
+ PermissionFlag::Attributes
+ } else {
+ PermissionFlag::Rwx
+ };
+ assert_eq!(expected, PermissionFlag::default());
}
#[test]
@@ -100,6 +109,16 @@ mod test {
}
#[test]
+ fn test_from_cli_attributes() {
+ let argv = ["lsd", "--permission", "attributes"];
+ let cli = Cli::try_parse_from(argv).unwrap();
+ assert_eq!(
+ Some(PermissionFlag::Attributes),
+ PermissionFlag::from_cli(&cli)
+ );
+ }
+
+ #[test]
fn test_from_cli_permissions_disable() {
let argv = ["lsd", "--permission", "disable"];
let cli = Cli::try_parse_from(argv).unwrap();
diff --git a/src/icon.rs b/src/icon.rs
index d07eca2..6fec005 100644
--- a/src/icon.rs
+++ b/src/icon.rs
@@ -75,7 +75,7 @@ impl Icons {
#[cfg(test)]
mod test {
use super::{IconTheme, Icons};
- use crate::flags::{IconOption, IconTheme as FlagTheme};
+ use crate::flags::{IconOption, IconTheme as FlagTheme, PermissionFlag};
use crate::meta::Meta;
use std::fs::File;
use tempfile::tempdir;
@@ -85,7 +85,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icons = Icons::new(true, IconOption::Never, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name);
@@ -97,7 +97,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icons = Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name);
@@ -110,7 +110,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icons = Icons::new(false, IconOption::Auto, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name);
@@ -122,7 +122,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icons = Icons::new(true, IconOption::Auto, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name);
@@ -135,7 +135,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(true, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name);
@@ -148,7 +148,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name);
@@ -161,7 +161,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(
false,
@@ -178,7 +178,7 @@ mod test {
fn get_icon_default_directory() {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path();
- let meta = Meta::from_path(file_path, false, false).unwrap();
+ let meta = Meta::from_path(file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name);
@@ -190,7 +190,7 @@ mod test {
fn get_icon_default_directory_unicode() {
let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path();
- let meta = Meta::from_path(file_path, false, false).unwrap();
+ let meta = Meta::from_path(file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(
false,
@@ -210,7 +210,7 @@ mod test {
for (file_name, file_icon) in &IconTheme::get_default_icons_by_name() {
let file_path = tmp_dir.path().join(file_name);
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name);
@@ -226,7 +226,7 @@ mod test {
for (ext, file_icon) in &IconTheme::get_default_icons_by_extension() {
let file_path = tmp_dir.path().join(format!("file.{ext}"));
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let icon = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name);
diff --git a/src/meta/filetype.rs b/src/meta/filetype.rs
index 61f6348..173331d 100644
--- a/src/meta/filetype.rs
+++ b/src/meta/filetype.rs
@@ -110,6 +110,10 @@ mod test {
use super::FileType;
use crate::color::{Colors, ThemeOption};
#[cfg(unix)]
+ use crate::flags::PermissionFlag;
+ #[cfg(unix)]
+ use crate::meta::permissions_or_attributes::PermissionsOrAttributes;
+ #[cfg(unix)]
use crate::meta::Permissions;
use crossterm::style::{Color, Stylize};
use std::fs::File;
@@ -144,14 +148,19 @@ mod test {
fn test_dir_type() {
let tmp_dir = tempdir().expect("failed to create temp dir");
#[cfg(not(windows))]
- let meta = crate::meta::Meta::from_path(tmp_dir.path(), false, false)
+ let meta = crate::meta::Meta::from_path(tmp_dir.path(), false, PermissionFlag::Rwx)
.expect("failed to get tempdir path");
let metadata = tmp_dir.path().metadata().expect("failed to get metas");
let colors = Colors::new(ThemeOption::NoLscolors);
#[cfg(not(windows))]
- let file_type = FileType::new(&metadata, None, &meta.permissions.unwrap());
+ let file_type = match meta.permissions_or_attributes {
+ Some(PermissionsOrAttributes::Permissions(permissions)) => {
+ FileType::new(&metadata, None, &permissions)
+ }
+ _ => panic!("unexpected"),
+ };
#[cfg(windows)]
let file_type = FileType::new(&metadata, None, tmp_dir.path());
diff --git a/src/meta/mod.rs b/src/meta/mod.rs
index 1f5d36d..1b0e4eb 100644
--- a/src/meta/mod.rs
+++ b/src/meta/mod.rs
@@ -9,8 +9,10 @@ mod locale;
pub mod name;
mod owner;
mod permissions;
+mod permissions_or_attributes;
mod size;
mod symlink;
+mod windows_attributes;
#[cfg(windows)]
mod windows_utils;
@@ -25,6 +27,7 @@ pub use self::links::Links;
pub use self::name::Name;
pub use self::owner::Owner;
pub use self::permissions::Permissions;
+use self::permissions_or_attributes::PermissionsOrAttributes;
pub use self::size::Size;
pub use self::symlink::SymLink;
@@ -35,11 +38,13 @@ use crate::git::GitCache;
use std::io::{self, Error, ErrorKind};
use std::path::{Component, Path, PathBuf};
+#[cfg(windows)]
+use self::windows_attributes::get_attributes;
#[derive(Clone, Debug)]
pub struct Meta {
pub name: Name,
pub path: PathBuf,
- pub permissions: Option<Permissions>,
+ pub permissions_or_attributes: Option<PermissionsOrAttributes>,
pub date: Option<Date>,
pub owner: Option<Owner>,
pub file_type: FileType,
@@ -97,7 +102,7 @@ impl Meta {
let mut parent_meta = Self::from_path(
&self.path.join(Component::ParentDir),
flags.dereference.0,
- flags.permission == PermissionFlag::Disable,
+ flags.permission,
)?;
parent_meta.name.name = "..".to_owned();
@@ -141,11 +146,8 @@ impl Meta {
_ => {}
}
- let mut entry_meta = match Self::from_path(
- &path,
- flags.dereference.0,
- flags.permission == PermissionFlag::Disable,
- ) {
+ let mut entry_meta = match Self::from_path(&path, flags.dereference.0, flags.permission)
+ {
Ok(res) => res,
Err(err) => {
print_error!("{}: {}.", path.display(), err);
@@ -250,7 +252,11 @@ impl Meta {
}
}
- pub fn from_path(path: &Path, dereference: bool, disable_permission: bool) -> io::Result<Self> {
+ pub fn from_path(
+ path: &Path,
+ dereference: bool,
+ permission_flag: PermissionFlag,
+ ) -> io::Result<Self> {
let mut metadata = path.symlink_metadata()?;
let mut symlink_meta = None;
let mut broken_link = false;
@@ -275,21 +281,30 @@ impl Meta {
}
#[cfg(unix)]
- let (owner, permissions) = if disable_permission {
- (None, None)
- } else {
- (
+ let (owner, permissions) = match permission_flag {
+ PermissionFlag::Disable => (None, None),
+ _ => (
Some(Owner::from(&metadata)),
Some(Permissions::from(&metadata)),
- )
+ ),
};
+ #[cfg(unix)]
+ let permissions_or_attributes = permissions.map(PermissionsOrAttributes::Permissions);
#[cfg(windows)]
- let (owner, permissions) = if disable_permission {
- (None, None)
- } else {
- match windows_utils::get_file_data(path) {
- Ok((owner, permissions)) => (Some(owner), Some(permissions)),
+ let (owner, permissions_or_attributes) = match permission_flag {
+ PermissionFlag::Disable => (None, None),
+ PermissionFlag::Attributes => (
+ None,
+ Some(PermissionsOrAttributes::WindowsAttributes(get_attributes(
+ &metadata,
+ ))),
+ ),
+ _ => match windows_utils::get_file_data(path) {
+ Ok((owner, permissions)) => (
+ Some(owner),
+ Some(PermissionsOrAttributes::Permissions(permissions)),
+ ),
Err(e) => {
eprintln!(
"lsd: {}: {}(Hint: Consider using `--permission disabled`.)",
@@ -298,7 +313,7 @@ impl Meta {
);
(None, None)
}
- }
+ },
};
#[cfg(not(windows))]
@@ -313,18 +328,19 @@ impl Meta {
let name = Name::new(path, file_type);
- let (inode, links, size, date, owner, permissions, access_control) = match broken_link {
- true => (None, None, None, None, None, None, None),
- false => (
- Some(INode::from(&metadata)),
- Some(Links::from(&metadata)),
- Some(Size::from(&metadata)),
- Some(Date::from(&metadata)),
- Some(owner),
- Some(permissions),
- Some(AccessControl::for_path(path)),
- ),
- };
+ let (inode, links, size, date, owner, permissions_or_attributes, access_control) =
+ match broken_link {
+ true => (None, None, None, None, None, None, None),
+ false => (
+ Some(INode::from(&metadata)),
+ Some(Links::from(&metadata)),
+ Some(Size::from(&metadata)),
+ Some(Date::from(&metadata)),
+ Some(owner),
+ Some(permissions_or_attributes),
+ Some(AccessControl::for_path(path)),
+ ),
+ };
Ok(Self {
inode,
@@ -335,7 +351,7 @@ impl Meta {
date,
indicator: Indicator::from(file_type),
owner: owner.unwrap_or_default(),
- permissions: permissions.unwrap_or_default(),
+ permissions_or_attributes: permissions_or_attributes.unwrap_or_default(),
name,
file_type,
content: None,
@@ -347,6 +363,8 @@ impl Meta {
#[cfg(test)]
mod tests {
+ use crate::flags::PermissionFlag;
+
use super::Meta;
use std::fs::File;
use tempfile::tempdir;
@@ -355,15 +373,15 @@ mod tests {
#[test]
fn test_from_path_path() {
let dir = assert_fs::TempDir::new().unwrap();
- let meta = Meta::from_path(dir.path(), false, false).unwrap();
+ let meta = Meta::from_path(dir.path(), false, PermissionFlag::Rwx).unwrap();
assert_eq!(meta.path, dir.path())
}
#[test]
fn test_from_path_disable_permission() {
let dir = assert_fs::TempDir::new().unwrap();
- let meta = Meta::from_path(dir.path(), false, true).unwrap();
- assert!(meta.permissions.is_none());
+ let meta = Meta::from_path(dir.path(), false, PermissionFlag::Disable).unwrap();
+ assert!(meta.permissions_or_attributes.is_none());
assert!(meta.owner.is_none());
}
@@ -373,7 +391,8 @@ mod tests {
let path_a = tmp_dir.path().join("aaa.aa");
File::create(&path_a).expect("failed to create file");
- let meta_a = Meta::from_path(&path_a, false, false).expect("failed to get meta");
+ let meta_a =
+ Meta::from_path(&path_a, false, PermissionFlag::Rwx).expect("failed to get meta");
let path_b = tmp_dir.path().join("bbb.bb");
let path_c = tmp_dir.path().join("ccc.cc");
@@ -388,7 +407,8 @@ mod tests {
std::os::windows::fs::symlink_file(path_c, &path_b)
.expect("failed to create broken symlink");
- let meta_b = Meta::from_path(&path_b, true, false).expect("failed to get meta");
+ let meta_b =
+ Meta::from_path(&path_b, true, PermissionFlag::Rwx).expect("failed to get meta");
assert!(
meta_a.inode.is_some()
@@ -396,7 +416,7 @@ mod tests {
&& meta_a.size.is_some()
&& meta_a.date.is_some()
&& meta_a.owner.is_some()
- && meta_a.permissions.is_some()
+ && meta_a.permissions_or_attributes.is_some()
&& meta_a.access_control.is_some()
);
@@ -406,7 +426,7 @@ mod tests {
&& meta_b.size.is_none()
&& meta_b.date.is_none()
&& meta_b.owner.is_none()
- && meta_b.permissions.is_none()
+ && meta_b.permissions_or_attributes.is_none()
&& meta_b.access_control.is_none()
);
}
diff --git a/src/meta/name.rs b/src/meta/name.rs
index 3b326c8..4d6ea6c 100644
--- a/src/meta/name.rs
+++ b/src/meta/name.rs
@@ -222,6 +222,7 @@ mod test {
use super::DisplayOption;
use super::Name;
use crate::color::{self, Colors};
+ use crate::flags::PermissionFlag;
use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme};
use crate::icon::Icons;
use crate::meta::FileType;
@@ -274,7 +275,7 @@ mod test {
// Create the directory
let dir_path = tmp_dir.path().join("directory");
fs::create_dir(&dir_path).expect("failed to create the dir");
- let meta = Meta::from_path(&dir_path, false, false).unwrap();
+ let meta = Meta::from_path(&dir_path, false, PermissionFlag::Rwx).unwrap();
let colors = Colors::new(color::ThemeOption::NoLscolors);
@@ -398,7 +399,7 @@ mod test {
// Create the file;
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let colors = Colors::new(color::ThemeOption::NoColor);
@@ -424,7 +425,7 @@ mod test {
// Create the file;
let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create file");
- let meta = Meta::from_path(&file_path, false, false).unwrap();
+ let meta = Meta::from_path(&file_path, false, PermissionFlag::Rwx).unwrap();
let colors = Colors::new(color::ThemeOption::NoColor);
diff --git a/src/meta/permissions.rs b/src/meta/permissions.rs
index af21a13..323675d 100644
--- a/src/meta/permissions.rs
+++ b/src/meta/permissions.rs
@@ -122,6 +122,8 @@ impl Permissions {
colors.colorize(octals, &Elem::Octal).to_string()
}
+ // technically this should be an error, hmm
+ PermissionFlag::Attributes => colors.colorize('-', &Elem::NoAccess).to_string(),
PermissionFlag::Disable => colors.colorize('-', &Elem::NoAccess).to_string(),
};
diff --git a/src/meta/permissions_or_attributes.rs b/src/meta/permissions_or_attributes.rs
new file mode 100644
index 0000000..d1c7347
--- /dev/null
+++ b/src/meta/permissions_or_attributes.rs
@@ -0,0 +1,27 @@
+#[cfg(windows)]
+use super::windows_attributes::WindowsAttributes;
+use crate::{
+ color::{ColoredString, Colors},
+ flags::Flags,
+};
+
+use super::Permissions;
+
+#[derive(Clone, Debug)]
+pub enum PermissionsOrAttributes {
+ Permissions(Permissions),
+ #[cfg(windows)]
+ WindowsAttributes(WindowsAttributes),
+}
+
+impl PermissionsOrAttributes {
+ pub fn render(&self, colors: &Colors, flags: &Flags) -> ColoredString {
+ match self {
+ PermissionsOrAttributes::Permissions(permissions) => permissions.render(colors, flags),
+ #[cfg(windows)]
+ PermissionsOrAttributes::WindowsAttributes(attributes) => {
+ attributes.render(colors, flags)
+ }
+ }
+ }
+}
diff --git a/src/meta/windows_attributes.rs b/src/meta/windows_attributes.rs
new file mode 100644
index 0000000..fa4a031
--- /dev/null
+++ b/src/meta/windows_attributes.rs
@@ -0,0 +1,126 @@
+#[cfg(windows)]
+use crate::{
+ color::{ColoredString, Colors, Elem},
+ flags::Flags,
+};
+#[cfg(windows)]
+use std::os::windows::fs::MetadataExt;
+
+#[cfg(windows)]
+#[derive(Debug, Clone)]
+pub struct WindowsAttributes {
+ pub archive: bool,
+ pub readonly: bool,
+ pub hidden: bool,
+ pub system: bool,
+}
+
+#[cfg(windows)]
+pub fn get_attributes(metadata: &std::fs::Metadata) -> WindowsAttributes {
+ use windows::Win32::Storage::FileSystem::{
+ FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_READONLY,
+ FILE_ATTRIBUTE_SYSTEM, FILE_FLAGS_AND_ATTRIBUTES,
+ };
+
+ let bits = metadata.file_attributes();
+ let has_bit = |bit: FILE_FLAGS_AND_ATTRIBUTES| bits & bit.0 == bit.0;
+
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+ WindowsAttributes {
+ archive: has_bit(FILE_ATTRIBUTE_ARCHIVE),
+ readonly: has_bit(FILE_ATTRIBUTE_READONLY),
+ hidden: has_bit(FILE_ATTRIBUTE_HIDDEN),
+ system: has_bit(FILE_ATTRIBUTE_SYSTEM),
+ }
+}
+
+#[cfg(windows)]
+impl WindowsAttributes {
+ pub fn render(&self, colors: &Colors, _flags: &Flags) -> ColoredString {
+ let res = [
+ match self.archive {
+ true => colors.colorize("a", &Elem::Archive),
+ false => colors.colorize('-', &Elem::NoAccess),
+ },
+ match self.readonly {
+ true => colors.colorize("r", &Elem::AttributeRead),
+ false => colors.colorize('-', &Elem::NoAccess),
+ },
+ match self.hidden {
+ true => colors.colorize("h", &Elem::Hidden),
+ false => colors.colorize('-', &Elem::NoAccess),
+ },
+ match self.system {
+ true => colors.colorize("s", &Elem::System),
+ false => colors.colorize('-', &Elem::NoAccess),
+ },
+ ]
+ .into_iter()
+ .fold(String::with_capacity(4), |mut acc, x| {
+ acc.push_str(&x.to_string());
+ acc
+ });
+ ColoredString::new(Colors::default_style(), res)
+ }
+}
+
+#[cfg(windows)]
+#[cfg(test)]
+mod test {
+ use std::fs;
+ use std::io::Write;
+ use std::process::Command;
+