From d0a55b4cbd1a01b12d64740a0af578f0fa463dae Mon Sep 17 00:00:00 2001 From: Dom Slee Date: Sun, 24 Sep 2023 23:14:54 +1000 Subject: [PATCH 1/3] Implementation --- src/app.rs | 4 +- src/color.rs | 17 ++++ src/config_file.rs | 7 +- src/core.rs | 24 ++--- src/display.rs | 30 +++--- src/flags/permission.rs | 23 ++++- src/icon.rs | 24 ++--- src/meta/filetype.rs | 13 ++- src/meta/mod.rs | 100 ++++++++++++-------- src/meta/name.rs | 7 +- src/meta/permissions.rs | 2 + src/meta/permissions_or_attributes.rs | 27 ++++++ src/meta/windows_attributes.rs | 126 ++++++++++++++++++++++++++ src/sort.rs | 76 ++++++++++------ src/theme/color.rs | 27 ++++++ tests/integration.rs | 2 +- 16 files changed, 389 insertions(+), 120 deletions(-) create mode 100644 src/meta/permissions_or_attributes.rs create mode 100644 src/meta/windows_attributes.rs diff --git a/src/app.rs b/src/app.rs index 0bb69259b..4170029b1 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, /// How to display size [default: default] diff --git a/src/color.rs b/src/color.rs index 8cb93c1ba..9a3a14ba2 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 b32a1f1d4..49f2695c3 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 54ec086b7..d806ada24 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 7c1b71887..72515f63f 100644 --- a/src/display.rs +++ b/src/display.rs @@ -344,8 +344,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 { @@ -488,7 +490,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; @@ -683,7 +685,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() @@ -716,7 +718,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() @@ -757,7 +759,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() @@ -797,7 +799,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() @@ -828,7 +830,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() @@ -862,7 +864,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() @@ -892,11 +894,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), @@ -942,15 +944,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 2cced0bde..bbaa5623d 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] @@ -99,6 +108,16 @@ mod test { assert_eq!(Some(PermissionFlag::Octal), PermissionFlag::from_cli(&cli)); } + #[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"]; diff --git a/src/icon.rs b/src/icon.rs index d07eca29a..6fec005f2 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 61f63482c..173331d82 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 267927659..d4cce4bed 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; pub use crate::icon::Icons; @@ -36,11 +39,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, + pub permissions_or_attributes: Option, pub date: Option, pub owner: Option, pub file_type: FileType, @@ -98,7 +103,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(); @@ -142,11 +147,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); @@ -251,7 +253,11 @@ impl Meta { } } - pub fn from_path(path: &Path, dereference: bool, disable_permission: bool) -> io::Result { + pub fn from_path( + path: &Path, + dereference: bool, + permission_flag: PermissionFlag, + ) -> io::Result { let mut metadata = path.symlink_metadata()?; let mut symlink_meta = None; let mut broken_link = false; @@ -276,21 +282,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`.)", @@ -299,7 +314,7 @@ impl Meta { ); (None, None) } - } + }, }; #[cfg(not(windows))] @@ -314,18 +329,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, @@ -336,7 +352,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, @@ -348,6 +364,8 @@ impl Meta { #[cfg(test)] mod tests { + use crate::flags::PermissionFlag; + use super::Meta; use std::fs::File; use tempfile::tempdir; @@ -356,15 +374,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()); } @@ -374,7 +392,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"); @@ -386,10 +405,11 @@ mod tests { // likely to fail because of permission issue // see https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html #[cfg(windows)] - std::os::windows::fs::symlink_file(&path_c, &path_b) + 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() @@ -397,7 +417,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() ); @@ -407,7 +427,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 3b326c847..4d6ea6cb5 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 af21a13d1..323675df9 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 000000000..d1c7347c2 --- /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 000000000..fa4a0319f --- /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; + + use crate::{ + color::{Colors, ThemeOption}, + flags::Flags, + }; + + use super::get_attributes; + use tempfile::tempdir; + + #[test] + pub fn archived_file() { + let attribute_string = create_and_process_file_with_attributes("archived_file.txt", "+A"); + assert_eq!("a---", attribute_string); + } + + #[test] + pub fn readonly_file() { + let attribute_string = create_and_process_file_with_attributes("readonly_file.txt", "+R"); + assert_eq!("ar--", attribute_string); + } + + #[test] + pub fn hidden_file() { + let attribute_string = create_and_process_file_with_attributes("hidden_file.txt", "+H"); + assert_eq!("a-h-", attribute_string); + } + + #[test] + pub fn system_file() { + let attribute_string = create_and_process_file_with_attributes("system_file.txt", "+S"); + assert_eq!("a--s", attribute_string); + } + + fn create_and_process_file_with_attributes(name: &str, attrs: &str) -> String { + let tmp_dir = tempdir().expect("failed to create temp dir"); + let path = tmp_dir.path().join(name); + let mut file = fs::File::create(path.clone()).unwrap(); + writeln!(file, "Test content").unwrap(); + Command::new("attrib") + .arg(attrs) + .arg(&path) + .output() + .expect("able to set attributes"); + let metadata = file.metadata().expect("able to get metadata"); + + let colors = Colors::new(ThemeOption::NoColor); + + let attributes = get_attributes(&metadata); + attributes + .render(&colors, &Flags::default()) + .content() + .to_string() + } +} diff --git a/src/sort.rs b/src/sort.rs index 6986fa8f8..34deb948d 100644 --- a/src/sort.rs +++ b/src/sort.rs @@ -80,7 +80,7 @@ fn by_git_status(a: &Meta, b: &Meta) -> Ordering { #[cfg(test)] mod tests { use super::*; - use crate::flags::Flags; + use crate::flags::{Flags, PermissionFlag}; use std::fs::{create_dir, File}; use std::io::prelude::*; use std::process::Command; @@ -93,12 +93,14 @@ mod tests { // Create the file; let path_a = tmp_dir.path().join("zzz"); 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"); // Create a dir; let path_z = tmp_dir.path().join("aaa"); create_dir(&path_z).expect("failed to create dir"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.dir_grouping = DirGrouping::First; @@ -121,12 +123,14 @@ mod tests { // Create the file; let path_a = tmp_dir.path().join("zzz"); 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"); // Create a dir; let path_z = tmp_dir.path().join("aaa"); create_dir(&path_z).expect("failed to create dir"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.dir_grouping = DirGrouping::Last; @@ -147,12 +151,14 @@ mod tests { // Create the file; let path_a = tmp_dir.path().join("aaa"); 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"); // Create a dir; let path_z = tmp_dir.path().join("zzz"); create_dir(&path_z).expect("failed to create dir"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.dir_grouping = DirGrouping::None; @@ -175,12 +181,14 @@ mod tests { // Create the file; let path_a = tmp_dir.path().join("zzz"); 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"); // Create a dir; let path_z = tmp_dir.path().join("aaa"); create_dir(&path_z).expect("failed to create dir"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.dir_grouping = DirGrouping::None; @@ -203,7 +211,8 @@ mod tests { // Create the file; let path_a = tmp_dir.path().join("aaa"); 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"); // Create the file; let path_z = tmp_dir.path().join("zzz"); @@ -229,7 +238,8 @@ mod tests { .success(); assert!(success, "failed to change file timestamp"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.column = SortColumn::Time; @@ -251,22 +261,26 @@ mod tests { // Create the file with rs extension; let path_a = tmp_dir.path().join("aaa.rs"); 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"); // Create the file with rs extension; let path_z = tmp_dir.path().join("zzz.rs"); File::create(&path_z).expect("failed to create file"); - let meta_z = Meta::from_path(&path_z, false, false).expect("failed to get meta"); + let meta_z = + Meta::from_path(&path_z, false, PermissionFlag::Rwx).expect("failed to get meta"); // Create the file with js extension; let path_j = tmp_dir.path().join("zzz.js"); File::create(&path_j).expect("failed to create file"); - let meta_j = Meta::from_path(&path_j, false, false).expect("failed to get meta"); + let meta_j = + Meta::from_path(&path_j, false, PermissionFlag::Rwx).expect("failed to get meta"); // Create the file with txt extension; let path_t = tmp_dir.path().join("zzz.txt"); File::create(&path_t).expect("failed to create file"); - let meta_t = Meta::from_path(&path_t, false, false).expect("failed to get meta"); + let meta_t = + Meta::from_path(&path_t, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.column = SortColumn::Extension; @@ -288,15 +302,18 @@ mod tests { let path_a = tmp_dir.path().join("2"); 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("11"); File::create(&path_b).expect("failed to create file"); - let meta_b = Meta::from_path(&path_b, false, false).expect("failed to get meta"); + let meta_b = + Meta::from_path(&path_b, false, PermissionFlag::Rwx).expect("failed to get meta"); let path_c = tmp_dir.path().join("12"); File::create(&path_c).expect("failed to create file"); - let meta_c = Meta::from_path(&path_c, false, false).expect("failed to get meta"); + let meta_c = + Meta::from_path(&path_c, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.column = SortColumn::Version; @@ -314,19 +331,23 @@ 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("aaa"); create_dir(&path_b).expect("failed to create dir"); - let meta_b = Meta::from_path(&path_b, false, false).expect("failed to get meta"); + let meta_b = + Meta::from_path(&path_b, false, PermissionFlag::Rwx).expect("failed to get meta"); let path_c = tmp_dir.path().join("zzz.zz"); File::create(&path_c).expect("failed to create file"); - let meta_c = Meta::from_path(&path_c, false, false).expect("failed to get meta"); + let meta_c = + Meta::from_path(&path_c, false, PermissionFlag::Rwx).expect("failed to get meta"); let path_d = tmp_dir.path().join("zzz"); create_dir(&path_d).expect("failed to create dir"); - let meta_d = Meta::from_path(&path_d, false, false).expect("failed to get meta"); + let meta_d = + Meta::from_path(&path_d, false, PermissionFlag::Rwx).expect("failed to get meta"); let mut flags = Flags::default(); flags.sorting.column = SortColumn::None; @@ -359,14 +380,16 @@ mod tests { .expect("failed to create file") .write_all(b"1, 2, 3") .expect("failed to write to 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"); File::create(&path_b) .expect("failed to create file") .write_all(b"1, 2, 3, 4, 5, 6, 7, 8, 9, 10") .expect("failed to write file"); - let meta_b = Meta::from_path(&path_b, false, false).expect("failed to get meta"); + let meta_b = + Meta::from_path(&path_b, false, PermissionFlag::Rwx).expect("failed to get meta"); let path_c = tmp_dir.path().join("ccc.cc"); let path_d = tmp_dir.path().join("ddd.dd"); @@ -378,10 +401,11 @@ mod tests { // likely to fail because of permission issue // see https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html #[cfg(windows)] - std::os::windows::fs::symlink_file(&path_d, &path_c) + std::os::windows::fs::symlink_file(path_d, &path_c) .expect("failed to create broken symlink"); - let meta_c = Meta::from_path(&path_c, true, false).expect("failed to get meta"); + let meta_c = + Meta::from_path(&path_c, true, PermissionFlag::Rwx).expect("failed to get meta"); assert_eq!(by_size(&meta_a, &meta_a), Ordering::Equal); assert_eq!(by_size(&meta_a, &meta_b), Ordering::Greater); diff --git a/src/theme/color.rs b/src/theme/color.rs index 37afca276..dd9d39d9f 100644 --- a/src/theme/color.rs +++ b/src/theme/color.rs @@ -89,6 +89,7 @@ pub struct ColorTheme { #[serde(deserialize_with = "deserialize_color")] pub group: Color, pub permission: Permission, + pub attributes: Attributes, pub date: Date, pub size: Size, pub inode: INode, @@ -124,6 +125,21 @@ pub struct Permission { pub context: Color, } +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +#[serde(deny_unknown_fields)] +#[serde(default)] +pub struct Attributes { + #[serde(deserialize_with = "deserialize_color")] + pub archive: Color, + #[serde(deserialize_with = "deserialize_color")] + pub read: Color, + #[serde(deserialize_with = "deserialize_color")] + pub hidden: Color, + #[serde(deserialize_with = "deserialize_color")] + pub system: Color, +} + #[derive(Debug, Deserialize, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] #[serde(deny_unknown_fields)] @@ -274,6 +290,16 @@ impl Default for Permission { } } } +impl Default for Attributes { + fn default() -> Self { + Attributes { + archive: Color::DarkGreen, + read: Color::DarkYellow, + hidden: Color::AnsiValue(13), // Pink, + system: Color::AnsiValue(13), // Pink, + } + } +} impl Default for FileType { fn default() -> Self { FileType { @@ -381,6 +407,7 @@ impl ColorTheme { user: Color::AnsiValue(230), // Cornsilk1 group: Color::AnsiValue(187), // LightYellow3 permission: Permission::default(), + attributes: Attributes::default(), file_type: FileType::default(), date: Date::default(), size: Size::default(), diff --git a/tests/integration.rs b/tests/integration.rs index f2000577a..11ef41f0b 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -303,7 +303,7 @@ fn test_dereference_link_broken_link_output() { // likely to fail because of permission issue // see https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html #[cfg(windows)] - std::os::windows::fs::symlink_file(&target, &link).expect("failed to create broken symlink"); + std::os::windows::fs::symlink_file(target, &link).expect("failed to create broken symlink"); cmd() .arg("-l") From d88a55f97007dfc762f7bed596785fb4f83e8c72 Mon Sep 17 00:00:00 2001 From: Dom Slee Date: Fri, 29 Sep 2023 19:31:53 +1000 Subject: [PATCH 2/3] Update doc --- README.md | 4 ++-- doc/lsd.md | 2 +- src/app.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 00061d377..3ac822ea8 100644 --- a/README.md +++ b/README.md @@ -189,8 +189,8 @@ size: default # == Permission == # Specify the format of the permission column -# Possible value: rwx, octal -permission: rwx +# Possible value: rwx, octal, attributes (windows only), disabled +# permission: rwx # == Sorting == sorting: diff --git a/doc/lsd.md b/doc/lsd.md index 5cef23460..b929ccae8 100644 --- a/doc/lsd.md +++ b/doc/lsd.md @@ -123,7 +123,7 @@ lsd is a ls command with a lot of pretty colours and some other stuff to enrich : Do not display files/directories with names matching the glob pattern(s). More than one can be specified by repeating the argument [default: ] `--permission ...` -: How to display permissions [default: rwx] [possible values: rwx, octal] +: How to display permissions [default: rwx for linux, attributes for windows] [possible values: rwx, octal, attributes, disable] `--size ...` : How to display size [default: default] [possible values: default, short, bytes] diff --git a/src/app.rs b/src/app.rs index 4170029b1..7cbf91336 100644 --- a/src/app.rs +++ b/src/app.rs @@ -68,7 +68,7 @@ pub struct Cli { #[arg(short, long, conflicts_with_all = ["depth", "recursive"])] pub directory_only: bool, - /// How to display permissions [default: rwx for non-windows, attributes for windows] + /// How to display permissions [default: rwx for linux, attributes for windows] #[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "attributes", "disable"])] pub permission: Option, From 281c74a7b38982cf41b171d0af12f4e14873dfd4 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Fri, 16 Feb 2024 11:36:54 +0800 Subject: [PATCH 3/3] :art: :hammer: simplify the windows cfg Signed-off-by: Wei Zhang --- src/meta/mod.rs | 3 ++- src/meta/name.rs | 4 +--- src/meta/windows_attributes.rs | 7 +------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/meta/mod.rs b/src/meta/mod.rs index d4cce4bed..a97cabb4e 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -12,8 +12,9 @@ mod permissions; mod permissions_or_attributes; mod size; mod symlink; -mod windows_attributes; +#[cfg(windows)] +mod windows_attributes; #[cfg(windows)] mod windows_utils; diff --git a/src/meta/name.rs b/src/meta/name.rs index 4d6ea6cb5..81f28b03c 100644 --- a/src/meta/name.rs +++ b/src/meta/name.rs @@ -205,9 +205,7 @@ impl Ord for Name { impl PartialOrd for Name { fn partial_cmp(&self, other: &Self) -> Option { - self.name - .to_lowercase() - .partial_cmp(&other.name.to_lowercase()) + Some(self.cmp(other)) } } diff --git a/src/meta/windows_attributes.rs b/src/meta/windows_attributes.rs index fa4a0319f..6fb8a23b7 100644 --- a/src/meta/windows_attributes.rs +++ b/src/meta/windows_attributes.rs @@ -1,12 +1,10 @@ -#[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, @@ -15,7 +13,6 @@ pub struct WindowsAttributes { 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, @@ -34,7 +31,6 @@ pub fn get_attributes(metadata: &std::fs::Metadata) -> WindowsAttributes { } } -#[cfg(windows)] impl WindowsAttributes { pub fn render(&self, colors: &Colors, _flags: &Flags) -> ColoredString { let res = [ @@ -64,7 +60,6 @@ impl WindowsAttributes { } } -#[cfg(windows)] #[cfg(test)] mod test { use std::fs;