Implementation

This commit is contained in:
Dom Slee 2023-09-24 23:14:54 +10:00 committed by Wei Zhang
parent 48b5adf929
commit 92d4e96622
15 changed files with 386 additions and 117 deletions

View file

@ -68,8 +68,8 @@ pub struct Cli {
#[arg(short, long, conflicts_with_all = ["depth", "recursive"])] #[arg(short, long, conflicts_with_all = ["depth", "recursive"])]
pub directory_only: bool, pub directory_only: bool,
/// How to display permissions [default: rwx] /// How to display permissions [default: rwx for non-windows, attributes for windows]
#[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "disable"])] #[arg(long, value_name = "MODE", value_parser = ["rwx", "octal", "attributes", "disable"])]
pub permission: Option<String>, pub permission: Option<String>,
/// How to display size [default: default] /// How to display size [default: default]

View file

@ -38,6 +38,12 @@ pub enum Elem {
Acl, Acl,
Context, Context,
/// Attributes
Archive,
AttributeRead,
Hidden,
System,
/// Last Time Modified /// Last Time Modified
DayOld, DayOld,
HourOld, HourOld,
@ -112,6 +118,11 @@ impl Elem {
Elem::Acl => theme.permission.acl, Elem::Acl => theme.permission.acl,
Elem::Context => theme.permission.context, 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::DayOld => theme.date.day_old,
Elem::HourOld => theme.date.hour_old, Elem::HourOld => theme.date.hour_old,
Elem::Older => theme.date.older, Elem::Older => theme.date.older,
@ -399,6 +410,12 @@ mod elem {
acl: Color::DarkCyan, acl: Color::DarkCyan,
context: Color::Cyan, context: Color::Cyan,
}, },
attributes: color::Attributes {
read: Color::Green,
archive: Color::Yellow,
hidden: Color::Red,
system: Color::Magenta,
},
file_type: color::FileType { file_type: color::FileType {
file: color::File { file: color::File {
exec_uid: Color::AnsiValue(40), // Green3 exec_uid: Color::AnsiValue(40), // Green3

View file

@ -299,8 +299,8 @@ size: default
# == Permission == # == Permission ==
# Specify the format of the permission column. # Specify the format of the permission column.
# Possible value: rwx, octal # Possible value: rwx, octal, attributes, disable
permission: rwx # permission: rwx
# == Sorting == # == Sorting ==
sorting: sorting:
@ -363,7 +363,6 @@ mod tests {
use crate::flags::color::{ColorOption, ThemeOption}; use crate::flags::color::{ColorOption, ThemeOption};
use crate::flags::icons::{IconOption, IconTheme}; use crate::flags::icons::{IconOption, IconTheme};
use crate::flags::layout::Layout; use crate::flags::layout::Layout;
use crate::flags::permission::PermissionFlag;
use crate::flags::size::SizeFlag; use crate::flags::size::SizeFlag;
use crate::flags::sorting::{DirGrouping, SortColumn}; use crate::flags::sorting::{DirGrouping, SortColumn};
use crate::flags::HyperlinkOption; use crate::flags::HyperlinkOption;
@ -402,7 +401,7 @@ mod tests {
depth: None, depth: None,
}), }),
size: Some(SizeFlag::Default), size: Some(SizeFlag::Default),
permission: Some(PermissionFlag::Rwx), permission: None,
sorting: Some(config_file::Sorting { sorting: Some(config_file::Sorting {
column: Some(SortColumn::Name), column: Some(SortColumn::Name),
reverse: Some(false), reverse: Some(false),

View file

@ -1,8 +1,7 @@
use crate::color::Colors; use crate::color::Colors;
use crate::display; use crate::display;
use crate::flags::{ use crate::flags::{
ColorOption, Display, Flags, HyperlinkOption, Layout, Literal, PermissionFlag, SortOrder, ColorOption, Display, Flags, HyperlinkOption, Layout, Literal, SortOrder, ThemeOption,
ThemeOption,
}; };
use crate::git::GitCache; use crate::git::GitCache;
use crate::icon::Icons; use crate::icon::Icons;
@ -106,11 +105,8 @@ impl Core {
}; };
for path in paths { for path in paths {
let mut meta = match Meta::from_path( let mut meta =
&path, match Meta::from_path(&path, self.flags.dereference.0, self.flags.permission) {
self.flags.dereference.0,
self.flags.permission == PermissionFlag::Disable,
) {
Ok(meta) => meta, Ok(meta) => meta,
Err(err) => { Err(err) => {
print_error!("{}: {}.", path.display(), err); print_error!("{}: {}.", path.display(), err);

View file

@ -347,8 +347,10 @@ fn get_output(
Block::Permission => { Block::Permission => {
block_vec.extend([ block_vec.extend([
meta.file_type.render(colors), meta.file_type.render(colors),
match meta.permissions { match &meta.permissions_or_attributes {
Some(permissions) => permissions.render(colors, flags), Some(permissions_or_attributes) => {
permissions_or_attributes.render(colors, flags)
}
None => colorize_missing("?????????"), None => colorize_missing("?????????"),
}, },
match &meta.access_control { match &meta.access_control {
@ -491,7 +493,7 @@ mod tests {
use crate::app::Cli; use crate::app::Cli;
use crate::color; use crate::color;
use crate::color::Colors; 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::icon::Icons;
use crate::meta::{FileType, Name}; use crate::meta::{FileType, Name};
use crate::Config; use crate::Config;
@ -686,7 +688,7 @@ mod tests {
dir.child("one.d").create_dir_all().unwrap(); dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().unwrap(); dir.child("one.d/two").touch().unwrap();
dir.child("one.d/.hidden").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() .unwrap()
.recurse_into(42, &flags, None) .recurse_into(42, &flags, None)
.unwrap() .unwrap()
@ -719,7 +721,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap(); dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().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() .unwrap()
.recurse_into(42, &flags, None) .recurse_into(42, &flags, None)
.unwrap() .unwrap()
@ -760,7 +762,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
dir.child("dir").create_dir_all().unwrap(); dir.child("dir").create_dir_all().unwrap();
dir.child("dir/file").touch().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() .unwrap()
.recurse_into(42, &flags, None) .recurse_into(42, &flags, None)
.unwrap() .unwrap()
@ -800,7 +802,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
dir.child("one.d").create_dir_all().unwrap(); dir.child("one.d").create_dir_all().unwrap();
dir.child("one.d/two").touch().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() .unwrap()
.recurse_into(42, &flags, None) .recurse_into(42, &flags, None)
.unwrap() .unwrap()
@ -831,7 +833,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().unwrap(); dir.child("testdir").create_dir_all().unwrap();
dir.child("test").touch().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() .unwrap()
.recurse_into(1, &flags, None) .recurse_into(1, &flags, None)
.unwrap() .unwrap()
@ -865,7 +867,7 @@ mod tests {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
dir.child("testdir").create_dir_all().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() .unwrap()
.recurse_into(1, &flags, None) .recurse_into(1, &flags, None)
.unwrap() .unwrap()
@ -895,11 +897,11 @@ mod tests {
let file_path = tmp_dir.path().join("file"); let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the 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"); let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the 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!( assert_eq!(
display_folder_path(&dir), display_folder_path(&dir),
@ -945,15 +947,15 @@ mod tests {
let file_path = tmp_dir.path().join("file"); let file_path = tmp_dir.path().join("file");
std::fs::File::create(&file_path).expect("failed to create the 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"); let dir_path = tmp_dir.path().join("dir");
std::fs::create_dir(&dir_path).expect("failed to create the 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"); let link_path = tmp_dir.path().join("link");
std::os::unix::fs::symlink("dir", &link_path).unwrap(); 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 { let grid_flags = Flags {
layout: Layout::Grid, layout: Layout::Grid,

View file

@ -13,10 +13,13 @@ use serde::Deserialize;
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub enum PermissionFlag { pub enum PermissionFlag {
/// The variant to show file permissions in rwx format /// The variant to show file permissions in rwx format
#[default] #[cfg_attr(not(target_os = "windows"), default)]
Rwx, Rwx,
/// The variant to show file permissions in octal format /// The variant to show file permissions in octal format
Octal, 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 the display of owner and permissions, may be used to speed up in Windows
Disable, Disable,
} }
@ -26,6 +29,7 @@ impl PermissionFlag {
match value { match value {
"rwx" => Self::Rwx, "rwx" => Self::Rwx,
"octal" => Self::Octal, "octal" => Self::Octal,
"attributes" => Self::Attributes,
"disable" => Self::Disable, "disable" => Self::Disable,
// Invalid value should be handled by `clap` when building an `Cli` // Invalid value should be handled by `clap` when building an `Cli`
other => unreachable!("Invalid value '{other}' for 'permission'"), other => unreachable!("Invalid value '{other}' for 'permission'"),
@ -75,7 +79,12 @@ mod test {
#[test] #[test]
fn test_default() { 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] #[test]
@ -99,6 +108,16 @@ mod test {
assert_eq!(Some(PermissionFlag::Octal), PermissionFlag::from_cli(&cli)); 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] #[test]
fn test_from_cli_permissions_disable() { fn test_from_cli_permissions_disable() {
let argv = ["lsd", "--permission", "disable"]; let argv = ["lsd", "--permission", "disable"];

View file

@ -75,7 +75,7 @@ impl Icons {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{IconTheme, Icons}; use super::{IconTheme, Icons};
use crate::flags::{IconOption, IconTheme as FlagTheme}; use crate::flags::{IconOption, IconTheme as FlagTheme, PermissionFlag};
use crate::meta::Meta; use crate::meta::Meta;
use std::fs::File; use std::fs::File;
use tempfile::tempdir; use tempfile::tempdir;
@ -85,7 +85,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 icons = Icons::new(true, IconOption::Never, FlagTheme::Fancy, " ".to_string()); let icons = Icons::new(true, IconOption::Never, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name); let icon = icons.get(&meta.name);
@ -97,7 +97,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 icons = Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string()); let icons = Icons::new(false, IconOption::Never, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name); let icon = icons.get(&meta.name);
@ -110,7 +110,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 icons = Icons::new(false, IconOption::Auto, FlagTheme::Fancy, " ".to_string()); let icons = Icons::new(false, IconOption::Auto, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name); let icon = icons.get(&meta.name);
@ -122,7 +122,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 icons = Icons::new(true, IconOption::Auto, FlagTheme::Fancy, " ".to_string()); let icons = Icons::new(true, IconOption::Auto, FlagTheme::Fancy, " ".to_string());
let icon = icons.get(&meta.name); let icon = icons.get(&meta.name);
@ -135,7 +135,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file"); let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create 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 = Icons::new(true, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name); let icon_str = icon.get(&meta.name);
@ -148,7 +148,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file"); let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create 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 = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name); let icon_str = icon.get(&meta.name);
@ -161,7 +161,7 @@ mod test {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path().join("file"); let file_path = tmp_dir.path().join("file");
File::create(&file_path).expect("failed to create 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( let icon = Icons::new(
false, false,
@ -178,7 +178,7 @@ mod test {
fn get_icon_default_directory() { fn get_icon_default_directory() {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path(); 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 = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name); let icon_str = icon.get(&meta.name);
@ -190,7 +190,7 @@ mod test {
fn get_icon_default_directory_unicode() { fn get_icon_default_directory_unicode() {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
let file_path = tmp_dir.path(); 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( let icon = Icons::new(
false, false,
@ -210,7 +210,7 @@ mod test {
for (file_name, file_icon) in &IconTheme::get_default_icons_by_name() { for (file_name, file_icon) in &IconTheme::get_default_icons_by_name() {
let file_path = tmp_dir.path().join(file_name); let file_path = tmp_dir.path().join(file_name);
File::create(&file_path).expect("failed to create 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 = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name); let icon_str = icon.get(&meta.name);
@ -226,7 +226,7 @@ mod test {
for (ext, file_icon) in &IconTheme::get_default_icons_by_extension() { for (ext, file_icon) in &IconTheme::get_default_icons_by_extension() {
let file_path = tmp_dir.path().join(format!("file.{ext}")); let file_path = tmp_dir.path().join(format!("file.{ext}"));
File::create(&file_path).expect("failed to create 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 = Icons::new(false, IconOption::Always, FlagTheme::Fancy, " ".to_string());
let icon_str = icon.get(&meta.name); let icon_str = icon.get(&meta.name);

View file

@ -110,6 +110,10 @@ mod test {
use super::FileType; use super::FileType;
use crate::color::{Colors, ThemeOption}; use crate::color::{Colors, ThemeOption};
#[cfg(unix)] #[cfg(unix)]
use crate::flags::PermissionFlag;
#[cfg(unix)]
use crate::meta::permissions_or_attributes::PermissionsOrAttributes;
#[cfg(unix)]
use crate::meta::Permissions; use crate::meta::Permissions;
use crossterm::style::{Color, Stylize}; use crossterm::style::{Color, Stylize};
use std::fs::File; use std::fs::File;
@ -144,14 +148,19 @@ mod test {
fn test_dir_type() { fn test_dir_type() {
let tmp_dir = tempdir().expect("failed to create temp dir"); let tmp_dir = tempdir().expect("failed to create temp dir");
#[cfg(not(windows))] #[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"); .expect("failed to get tempdir path");
let metadata = tmp_dir.path().metadata().expect("failed to get metas"); let metadata = tmp_dir.path().metadata().expect("failed to get metas");
let colors = Colors::new(ThemeOption::NoLscolors); let colors = Colors::new(ThemeOption::NoLscolors);
#[cfg(not(windows))] #[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)] #[cfg(windows)]
let file_type = FileType::new(&metadata, None, tmp_dir.path()); let file_type = FileType::new(&metadata, None, tmp_dir.path());

View file

@ -9,8 +9,10 @@ mod locale;
pub mod name; pub mod name;
mod owner; mod owner;
mod permissions; mod permissions;
mod permissions_or_attributes;
mod size; mod size;
mod symlink; mod symlink;
mod windows_attributes;
#[cfg(windows)] #[cfg(windows)]
mod windows_utils; mod windows_utils;
@ -25,6 +27,7 @@ pub use self::links::Links;
pub use self::name::Name; pub use self::name::Name;
pub use self::owner::Owner; pub use self::owner::Owner;
pub use self::permissions::Permissions; pub use self::permissions::Permissions;
use self::permissions_or_attributes::PermissionsOrAttributes;
pub use self::size::Size; pub use self::size::Size;
pub use self::symlink::SymLink; pub use self::symlink::SymLink;
@ -35,11 +38,13 @@ use crate::git::GitCache;
use std::io::{self, Error, ErrorKind}; use std::io::{self, Error, ErrorKind};
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
#[cfg(windows)]
use self::windows_attributes::get_attributes;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Meta { pub struct Meta {
pub name: Name, pub name: Name,
pub path: PathBuf, pub path: PathBuf,
pub permissions: Option<Permissions>, pub permissions_or_attributes: Option<PermissionsOrAttributes>,
pub date: Option<Date>, pub date: Option<Date>,
pub owner: Option<Owner>, pub owner: Option<Owner>,
pub file_type: FileType, pub file_type: FileType,
@ -97,7 +102,7 @@ impl Meta {
let mut parent_meta = Self::from_path( let mut parent_meta = Self::from_path(
&self.path.join(Component::ParentDir), &self.path.join(Component::ParentDir),
flags.dereference.0, flags.dereference.0,
flags.permission == PermissionFlag::Disable, flags.permission,
)?; )?;
parent_meta.name.name = "..".to_owned(); parent_meta.name.name = "..".to_owned();
@ -141,11 +146,8 @@ impl Meta {
_ => {} _ => {}
} }
let mut entry_meta = match Self::from_path( let mut entry_meta = match Self::from_path(&path, flags.dereference.0, flags.permission)
&path, {
flags.dereference.0,
flags.permission == PermissionFlag::Disable,
) {
Ok(res) => res, Ok(res) => res,
Err(err) => { Err(err) => {
print_error!("{}: {}.", path.display(), 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 metadata = path.symlink_metadata()?;
let mut symlink_meta = None; let mut symlink_meta = None;
let mut broken_link = false; let mut broken_link = false;
@ -275,21 +281,30 @@ impl Meta {
} }
#[cfg(unix)] #[cfg(unix)]
let (owner, permissions) = if disable_permission { let (owner, permissions) = match permission_flag {
(None, None) PermissionFlag::Disable => (None, None),
} else { _ => (
(
Some(Owner::from(&metadata)), Some(Owner::from(&metadata)),
Some(Permissions::from(&metadata)), Some(Permissions::from(&metadata)),
) ),
}; };
#[cfg(unix)]
let permissions_or_attributes = permissions.map(PermissionsOrAttributes::Permissions);
#[cfg(windows)] #[cfg(windows)]
let (owner, permissions) = if disable_permission { let (owner, permissions_or_attributes) = match permission_flag {
(None, None) PermissionFlag::Disable => (None, None),
} else { PermissionFlag::Attributes => (
match windows_utils::get_file_data(path) { None,
Ok((owner, permissions)) => (Some(owner), Some(permissions)), Some(PermissionsOrAttributes::WindowsAttributes(get_attributes(
&metadata,
))),
),
_ => match windows_utils::get_file_data(path) {
Ok((owner, permissions)) => (
Some(owner),
Some(PermissionsOrAttributes::Permissions(permissions)),
),
Err(e) => { Err(e) => {
eprintln!( eprintln!(
"lsd: {}: {}(Hint: Consider using `--permission disabled`.)", "lsd: {}: {}(Hint: Consider using `--permission disabled`.)",
@ -298,7 +313,7 @@ impl Meta {
); );
(None, None) (None, None)
} }
} },
}; };
#[cfg(not(windows))] #[cfg(not(windows))]
@ -313,7 +328,8 @@ impl Meta {
let name = Name::new(path, file_type); let name = Name::new(path, file_type);
let (inode, links, size, date, owner, permissions, access_control) = match broken_link { let (inode, links, size, date, owner, permissions_or_attributes, access_control) =
match broken_link {
true => (None, None, None, None, None, None, None), true => (None, None, None, None, None, None, None),
false => ( false => (
Some(INode::from(&metadata)), Some(INode::from(&metadata)),
@ -321,7 +337,7 @@ impl Meta {
Some(Size::from(&metadata)), Some(Size::from(&metadata)),
Some(Date::from(&metadata)), Some(Date::from(&metadata)),
Some(owner), Some(owner),
Some(permissions), Some(permissions_or_attributes),
Some(AccessControl::for_path(path)), Some(AccessControl::for_path(path)),
), ),
}; };
@ -335,7 +351,7 @@ impl Meta {
date, date,
indicator: Indicator::from(file_type), indicator: Indicator::from(file_type),
owner: owner.unwrap_or_default(), owner: owner.unwrap_or_default(),
permissions: permissions.unwrap_or_default(), permissions_or_attributes: permissions_or_attributes.unwrap_or_default(),
name, name,
file_type, file_type,
content: None, content: None,
@ -347,6 +363,8 @@ impl Meta {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::flags::PermissionFlag;
use super::Meta; use super::Meta;
use std::fs::File; use std::fs::File;
use tempfile::tempdir; use tempfile::tempdir;
@ -355,15 +373,15 @@ mod tests {
#[test] #[test]
fn test_from_path_path() { fn test_from_path_path() {
let dir = assert_fs::TempDir::new().unwrap(); 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()) assert_eq!(meta.path, dir.path())
} }
#[test] #[test]
fn test_from_path_disable_permission() { fn test_from_path_disable_permission() {
let dir = assert_fs::TempDir::new().unwrap(); let dir = assert_fs::TempDir::new().unwrap();
let meta = Meta::from_path(dir.path(), false, true).unwrap(); let meta = Meta::from_path(dir.path(), false, PermissionFlag::Disable).unwrap();
assert!(meta.permissions.is_none()); assert!(meta.permissions_or_attributes.is_none());
assert!(meta.owner.is_none()); assert!(meta.owner.is_none());
} }
@ -373,7 +391,8 @@ mod tests {
let path_a = tmp_dir.path().join("aaa.aa"); let path_a = tmp_dir.path().join("aaa.aa");
File::create(&path_a).expect("failed to create file"); 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_b = tmp_dir.path().join("bbb.bb");
let path_c = tmp_dir.path().join("ccc.cc"); 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) std::os::windows::fs::symlink_file(path_c, &path_b)
.expect("failed to create broken symlink"); .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!( assert!(
meta_a.inode.is_some() meta_a.inode.is_some()
@ -396,7 +416,7 @@ mod tests {
&& meta_a.size.is_some() && meta_a.size.is_some()
&& meta_a.date.is_some() && meta_a.date.is_some()
&& meta_a.owner.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() && meta_a.access_control.is_some()
); );
@ -406,7 +426,7 @@ mod tests {
&& meta_b.size.is_none() && meta_b.size.is_none()
&& meta_b.date.is_none() && meta_b.date.is_none()
&& meta_b.owner.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() && meta_b.access_control.is_none()
); );
} }

View file

@ -222,6 +222,7 @@ mod test {
use super::DisplayOption; use super::DisplayOption;
use super::Name; use super::Name;
use crate::color::{self, Colors}; use crate::color::{self, Colors};
use crate::flags::PermissionFlag;
use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme}; use crate::flags::{HyperlinkOption, IconOption, IconTheme as FlagTheme};
use crate::icon::Icons; use crate::icon::Icons;
use crate::meta::FileType; use crate::meta::FileType;
@ -274,7 +275,7 @@ mod test {
// Create the directory // Create the directory
let dir_path = tmp_dir.path().join("directory"); let dir_path = tmp_dir.path().join("directory");
fs::create_dir(&dir_path).expect("failed to create the dir"); 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); let colors = Colors::new(color::ThemeOption::NoLscolors);
@ -398,7 +399,7 @@ mod test {
// Create the file; // Create the file;
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 colors = Colors::new(color::ThemeOption::NoColor); let colors = Colors::new(color::ThemeOption::NoColor);
@ -424,7 +425,7 @@ mod test {
// Create the file; // Create the file;
let file_path = tmp_dir.path().join("file.txt"); let file_path = tmp_dir.path().join("file.txt");
File::create(&file_path).expect("failed to create 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 colors = Colors::new(color::ThemeOption::NoColor); let colors = Colors::new(color::ThemeOption::NoColor);

View file

@ -122,6 +122,8 @@ impl Permissions {
colors.colorize(octals, &Elem::Octal).to_string() 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(), PermissionFlag::Disable => colors.colorize('-', &Elem::NoAccess).to_string(),
}; };

View file

@ -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)
}
}
}
}

View file

@ -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()
}
}

View file

@ -80,7 +80,7 @@ fn by_git_status(a: &Meta, b: &Meta) -> Ordering {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::flags::Flags; use crate::flags::{Flags, PermissionFlag};
use std::fs::{create_dir, File}; use std::fs::{create_dir, File};
use std::io::prelude::*; use std::io::prelude::*;
use std::process::Command; use std::process::Command;
@ -93,12 +93,14 @@ mod tests {
// Create the file; // Create the file;
let path_a = tmp_dir.path().join("zzz"); let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file"); 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; // Create a dir;
let path_z = tmp_dir.path().join("aaa"); let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir"); 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(); let mut flags = Flags::default();
flags.sorting.dir_grouping = DirGrouping::First; flags.sorting.dir_grouping = DirGrouping::First;
@ -121,12 +123,14 @@ mod tests {
// Create the file; // Create the file;
let path_a = tmp_dir.path().join("zzz"); let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file"); 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; // Create a dir;
let path_z = tmp_dir.path().join("aaa"); let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir"); 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(); let mut flags = Flags::default();
flags.sorting.dir_grouping = DirGrouping::Last; flags.sorting.dir_grouping = DirGrouping::Last;
@ -147,12 +151,14 @@ mod tests {
// Create the file; // Create the file;
let path_a = tmp_dir.path().join("aaa"); let path_a = tmp_dir.path().join("aaa");
File::create(&path_a).expect("failed to create file"); 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; // Create a dir;
let path_z = tmp_dir.path().join("zzz"); let path_z = tmp_dir.path().join("zzz");
create_dir(&path_z).expect("failed to create dir"); 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(); let mut flags = Flags::default();
flags.sorting.dir_grouping = DirGrouping::None; flags.sorting.dir_grouping = DirGrouping::None;
@ -175,12 +181,14 @@ mod tests {
// Create the file; // Create the file;
let path_a = tmp_dir.path().join("zzz"); let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file"); 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; // Create a dir;
let path_z = tmp_dir.path().join("aaa"); let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir"); 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(); let mut flags = Flags::default();
flags.sorting.dir_grouping = DirGrouping::None; flags.sorting.dir_grouping = DirGrouping::None;
@ -203,7 +211,8 @@ mod tests {
// Create the file; // Create the file;
let path_a = tmp_dir.path().join("aaa"); let path_a = tmp_dir.path().join("aaa");
File::create(&path_a).expect("failed to create file"); 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; // Create the file;
let path_z = tmp_dir.path().join("zzz"); let path_z = tmp_dir.path().join("zzz");
@ -229,7 +238,8 @@ mod tests {
.success(); .success();
assert!(success, "failed to change file timestamp"); 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(); let mut flags = Flags::default();
flags.sorting.column = SortColumn::Time; flags.sorting.column = SortColumn::Time;
@ -251,22 +261,26 @@ mod tests {
// Create the file with rs extension; // Create the file with rs extension;
let path_a = tmp_dir.path().join("aaa.rs"); let path_a = tmp_dir.path().join("aaa.rs");
File::create(&path_a).expect("failed to create file"); 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; // Create the file with rs extension;
let path_z = tmp_dir.path().join("zzz.rs"); let path_z = tmp_dir.path().join("zzz.rs");
File::create(&path_z).expect("failed to create file"); 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; // Create the file with js extension;
let path_j = tmp_dir.path().join("zzz.js"); let path_j = tmp_dir.path().join("zzz.js");
File::create(&path_j).expect("failed to create file"); 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; // Create the file with txt extension;
let path_t = tmp_dir.path().join("zzz.txt"); let path_t = tmp_dir.path().join("zzz.txt");
File::create(&path_t).expect("failed to create file"); 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(); let mut flags = Flags::default();
flags.sorting.column = SortColumn::Extension; flags.sorting.column = SortColumn::Extension;
@ -288,15 +302,18 @@ mod tests {
let path_a = tmp_dir.path().join("2"); let path_a = tmp_dir.path().join("2");
File::create(&path_a).expect("failed to create file"); 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"); let path_b = tmp_dir.path().join("11");
File::create(&path_b).expect("failed to create file"); 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"); let path_c = tmp_dir.path().join("12");
File::create(&path_c).expect("failed to create file"); 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(); let mut flags = Flags::default();
flags.sorting.column = SortColumn::Version; flags.sorting.column = SortColumn::Version;
@ -314,19 +331,23 @@ mod tests {
let path_a = tmp_dir.path().join("aaa.aa"); let path_a = tmp_dir.path().join("aaa.aa");
File::create(&path_a).expect("failed to create file"); 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"); let path_b = tmp_dir.path().join("aaa");
create_dir(&path_b).expect("failed to create dir"); 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"); let path_c = tmp_dir.path().join("zzz.zz");
File::create(&path_c).expect("failed to create file"); 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"); let path_d = tmp_dir.path().join("zzz");
create_dir(&path_d).expect("failed to create dir"); 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(); let mut flags = Flags::default();
flags.sorting.column = SortColumn::None; flags.sorting.column = SortColumn::None;
@ -359,14 +380,16 @@ mod tests {
.expect("failed to create file") .expect("failed to create file")
.write_all(b"1, 2, 3") .write_all(b"1, 2, 3")
.expect("failed to write to file"); .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"); let path_b = tmp_dir.path().join("bbb.bb");
File::create(&path_b) File::create(&path_b)
.expect("failed to create file") .expect("failed to create file")
.write_all(b"1, 2, 3, 4, 5, 6, 7, 8, 9, 10") .write_all(b"1, 2, 3, 4, 5, 6, 7, 8, 9, 10")
.expect("failed to write file"); .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_c = tmp_dir.path().join("ccc.cc");
let path_d = tmp_dir.path().join("ddd.dd"); let path_d = tmp_dir.path().join("ddd.dd");
@ -381,7 +404,8 @@ mod tests {
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"); .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_a), Ordering::Equal);
assert_eq!(by_size(&meta_a, &meta_b), Ordering::Greater); assert_eq!(by_size(&meta_a, &meta_b), Ordering::Greater);

View file

@ -89,6 +89,7 @@ pub struct ColorTheme {
#[serde(deserialize_with = "deserialize_color")] #[serde(deserialize_with = "deserialize_color")]
pub group: Color, pub group: Color,
pub permission: Permission, pub permission: Permission,
pub attributes: Attributes,
pub date: Date, pub date: Date,
pub size: Size, pub size: Size,
pub inode: INode, pub inode: INode,
@ -124,6 +125,21 @@ pub struct Permission {
pub context: Color, 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)] #[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
#[serde(deny_unknown_fields)] #[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 { impl Default for FileType {
fn default() -> Self { fn default() -> Self {
FileType { FileType {
@ -381,6 +407,7 @@ impl ColorTheme {
user: Color::AnsiValue(230), // Cornsilk1 user: Color::AnsiValue(230), // Cornsilk1
group: Color::AnsiValue(187), // LightYellow3 group: Color::AnsiValue(187), // LightYellow3
permission: Permission::default(), permission: Permission::default(),
attributes: Attributes::default(),
file_type: FileType::default(), file_type: FileType::default(),
date: Date::default(), date: Date::default(),
size: Size::default(), size: Size::default(),