mirror of
https://github.com/lsd-rs/lsd
synced 2024-12-13 13:42:34 +00:00
arg: ✨ add support for dereference
This commit is contained in:
parent
8e1662d5e9
commit
faea1c0051
11 changed files with 76 additions and 38 deletions
|
@ -217,6 +217,13 @@ pub fn build() -> App<'static, 'static> {
|
|||
.multiple(true)
|
||||
.help("Display the index number of each file"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("dereference")
|
||||
.short("L")
|
||||
.long("dereference")
|
||||
.multiple(true)
|
||||
.help("When showing file information for a symbolic link, show information for the file the link references rather than for the link itself"),
|
||||
)
|
||||
}
|
||||
|
||||
fn validate_date_argument(arg: String) -> Result<(), String> {
|
||||
|
|
|
@ -52,10 +52,7 @@ pub enum Elem {
|
|||
|
||||
impl Elem {
|
||||
pub fn has_suid(&self) -> bool {
|
||||
match self {
|
||||
Elem::Dir { uid: true } | Elem::File { uid: true, .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, Elem::Dir { uid: true } | Elem::File { uid: true, .. })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ impl Core {
|
|||
};
|
||||
|
||||
for path in paths {
|
||||
let mut meta = match Meta::from_path(&path) {
|
||||
let mut meta = match Meta::from_path(&path, self.flags.dereference) {
|
||||
Ok(meta) => meta,
|
||||
Err(err) => {
|
||||
print_error!("lsd: {}: {}\n", path.display(), err);
|
||||
|
@ -95,7 +95,12 @@ impl Core {
|
|||
meta_list.push(meta);
|
||||
}
|
||||
_ => {
|
||||
match meta.recurse_into(depth, self.flags.display, &self.flags.ignore_globs) {
|
||||
match meta.recurse_into(
|
||||
depth,
|
||||
self.flags.display,
|
||||
&self.flags.ignore_globs,
|
||||
self.flags.dereference,
|
||||
) {
|
||||
Ok(content) => {
|
||||
meta.content = content;
|
||||
meta_list.push(meta);
|
||||
|
|
|
@ -221,10 +221,7 @@ fn should_display_folder_path(depth: usize, metas: &[Meta]) -> bool {
|
|||
} else {
|
||||
let folder_number = metas
|
||||
.iter()
|
||||
.filter(|x| match x.file_type {
|
||||
FileType::Directory { .. } => true,
|
||||
_ => false,
|
||||
})
|
||||
.filter(|x| matches!(x.file_type, FileType::Directory { .. }))
|
||||
.count();
|
||||
|
||||
folder_number > 1 || folder_number < metas.len()
|
||||
|
@ -270,7 +267,7 @@ fn get_output<'a>(
|
|||
Block::SizeValue => strings.push(meta.size.render_value(colors, flags)),
|
||||
Block::Date => strings.push(meta.date.render(colors, &flags)),
|
||||
Block::Name => {
|
||||
let s: String = if flags.no_symlink {
|
||||
let s: String = if flags.no_symlink || flags.dereference {
|
||||
ANSIStrings(&[
|
||||
meta.name.render(colors, icons, &display_option),
|
||||
meta.indicator.render(&flags),
|
||||
|
|
|
@ -22,6 +22,7 @@ pub struct Flags {
|
|||
pub no_symlink: bool,
|
||||
pub total_size: bool,
|
||||
pub ignore_globs: GlobSet,
|
||||
pub dereference: bool,
|
||||
}
|
||||
|
||||
impl Flags {
|
||||
|
@ -34,6 +35,7 @@ impl Flags {
|
|||
let date_inputs: Vec<&str> = matches.values_of("date").unwrap().collect();
|
||||
let dir_order_inputs: Vec<&str> = matches.values_of("group-dirs").unwrap().collect();
|
||||
let ignore_globs_inputs: Vec<&str> = matches.values_of("ignore-glob").unwrap().collect();
|
||||
let dereference = matches.is_present("dereference");
|
||||
// inode set layout to oneline and blocks to inode,name
|
||||
let inode = matches.is_present("inode");
|
||||
let blocks_inputs: Vec<&str> = if let Some(blocks) = matches.values_of("blocks") {
|
||||
|
@ -179,6 +181,7 @@ impl Flags {
|
|||
no_symlink: matches.is_present("no-symlink"),
|
||||
total_size: matches.is_present("total-size"),
|
||||
inode,
|
||||
dereference,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +207,7 @@ impl Default for Flags {
|
|||
total_size: false,
|
||||
ignore_globs: GlobSet::empty(),
|
||||
inode: false,
|
||||
dereference: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/icon.rs
16
src/icon.rs
|
@ -342,7 +342,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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::NoIcon);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -355,7 +355,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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Fancy);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -368,7 +368,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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Unicode);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -380,7 +380,7 @@ mod test {
|
|||
fn get_directory_icon() {
|
||||
let tmp_dir = tempdir().expect("failed to create temp dir");
|
||||
let file_path = tmp_dir.path();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf()).unwrap();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf(), false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Fancy);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -392,7 +392,7 @@ mod test {
|
|||
fn get_directory_icon_unicode() {
|
||||
let tmp_dir = tempdir().expect("failed to create temp dir");
|
||||
let file_path = tmp_dir.path();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf()).unwrap();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf(), false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Unicode);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -404,7 +404,7 @@ mod test {
|
|||
fn get_directory_icon_with_ext() {
|
||||
let tmp_dir = tempdir().expect("failed to create temp dir");
|
||||
let file_path = tmp_dir.path();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf()).unwrap();
|
||||
let meta = Meta::from_path(&file_path.to_path_buf(), false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Fancy);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -419,7 +419,7 @@ mod test {
|
|||
for (file_name, file_icon) in &Icons::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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Fancy);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
@ -435,7 +435,7 @@ mod test {
|
|||
for (ext, file_icon) in &Icons::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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let icon = Icons::new(Theme::Fancy);
|
||||
let icon = icon.get(&meta.name);
|
||||
|
|
|
@ -137,8 +137,8 @@ mod test {
|
|||
#[test]
|
||||
fn test_dir_type() {
|
||||
let tmp_dir = tempdir().expect("failed to create temp dir");
|
||||
let meta =
|
||||
Meta::from_path(&tmp_dir.path().to_path_buf()).expect("failed to get tempdir path");
|
||||
let meta = Meta::from_path(&tmp_dir.path().to_path_buf(), false)
|
||||
.expect("failed to get tempdir path");
|
||||
let metadata = tmp_dir.path().metadata().expect("failed to get metas");
|
||||
|
||||
let colors = Colors::new(Theme::NoLscolors);
|
||||
|
|
|
@ -51,6 +51,7 @@ impl Meta {
|
|||
depth: usize,
|
||||
display: Display,
|
||||
ignore_globs: &GlobSet,
|
||||
dereference: bool,
|
||||
) -> Result<Option<Vec<Meta>>, std::io::Error> {
|
||||
if depth == 0 {
|
||||
return Ok(None);
|
||||
|
@ -81,7 +82,7 @@ impl Meta {
|
|||
current_meta = self.clone();
|
||||
current_meta.name.name = ".".to_owned();
|
||||
|
||||
let parent_meta = Self::from_path(&self.path.join(Component::ParentDir))?;
|
||||
let parent_meta = Self::from_path(&self.path.join(Component::ParentDir), dereference)?;
|
||||
|
||||
content.push(current_meta);
|
||||
content.push(parent_meta);
|
||||
|
@ -104,7 +105,7 @@ impl Meta {
|
|||
}
|
||||
}
|
||||
|
||||
let mut entry_meta = match Self::from_path(&path) {
|
||||
let mut entry_meta = match Self::from_path(&path, dereference) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
print_error!("lsd: {}: {}\n", path.display(), err);
|
||||
|
@ -112,7 +113,7 @@ impl Meta {
|
|||
}
|
||||
};
|
||||
|
||||
match entry_meta.recurse_into(depth - 1, display, ignore_globs) {
|
||||
match entry_meta.recurse_into(depth - 1, display, ignore_globs, dereference) {
|
||||
Ok(content) => entry_meta.content = content,
|
||||
Err(err) => {
|
||||
print_error!("lsd: {}: {}\n", path.display(), err);
|
||||
|
@ -186,9 +187,9 @@ impl Meta {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_path(path: &Path) -> Result<Self, std::io::Error> {
|
||||
pub fn from_path(path: &Path, dereference: bool) -> Result<Self, std::io::Error> {
|
||||
// If the file is a link then retrieve link metadata instead with target metadata (if present).
|
||||
let (metadata, symlink_meta) = if read_link(path).is_ok() {
|
||||
let (metadata, symlink_meta) = if read_link(path).is_ok() && !dereference {
|
||||
(path.symlink_metadata()?, path.metadata().ok())
|
||||
} else {
|
||||
(path.metadata()?, None)
|
||||
|
|
|
@ -216,7 +216,7 @@ mod test {
|
|||
// Chreate 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).unwrap();
|
||||
let meta = Meta::from_path(&dir_path, false).unwrap();
|
||||
|
||||
let colors = Colors::new(color::Theme::NoLscolors);
|
||||
|
||||
|
@ -316,7 +316,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).unwrap();
|
||||
let meta = Meta::from_path(&file_path, false).unwrap();
|
||||
|
||||
let colors = Colors::new(color::Theme::NoColor);
|
||||
|
||||
|
|
20
src/sort.rs
20
src/sort.rs
|
@ -139,12 +139,12 @@ 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).expect("failed to get meta");
|
||||
let meta_a = Meta::from_path(&path_a, false).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).expect("failed to get meta");
|
||||
let meta_z = Meta::from_path(&path_z, false).expect("failed to get meta");
|
||||
|
||||
let mut flags = Flags::default();
|
||||
flags.directory_order = DirOrderFlag::First;
|
||||
|
@ -164,12 +164,12 @@ 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).expect("failed to get meta");
|
||||
let meta_a = Meta::from_path(&path_a, false).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).expect("failed to get meta");
|
||||
let meta_z = Meta::from_path(&path_z, false).expect("failed to get meta");
|
||||
|
||||
let mut flags = Flags::default();
|
||||
flags.directory_order = DirOrderFlag::Last;
|
||||
|
@ -188,12 +188,12 @@ 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).expect("failed to get meta");
|
||||
let meta_a = Meta::from_path(&path_a, false).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).expect("failed to get meta");
|
||||
let meta_z = Meta::from_path(&path_z, false).expect("failed to get meta");
|
||||
|
||||
let mut flags = Flags::default();
|
||||
flags.directory_order = DirOrderFlag::None;
|
||||
|
@ -213,12 +213,12 @@ 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).expect("failed to get meta");
|
||||
let meta_a = Meta::from_path(&path_a, false).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).expect("failed to get meta");
|
||||
let meta_z = Meta::from_path(&path_z, false).expect("failed to get meta");
|
||||
|
||||
let mut flags = Flags::default();
|
||||
flags.directory_order = DirOrderFlag::None;
|
||||
|
@ -238,7 +238,7 @@ 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).expect("failed to get meta");
|
||||
let meta_a = Meta::from_path(&path_a, false).expect("failed to get meta");
|
||||
|
||||
// Create the file;
|
||||
let path_z = tmp_dir.path().join("zzz");
|
||||
|
@ -264,7 +264,7 @@ mod tests {
|
|||
.success();
|
||||
|
||||
assert_eq!(true, success, "failed to change file timestamp");
|
||||
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
|
||||
let meta_z = Meta::from_path(&path_z, false).expect("failed to get meta");
|
||||
|
||||
let mut flags = Flags::default();
|
||||
flags.sort_by = SortFlag::Time;
|
||||
|
|
|
@ -157,6 +157,33 @@ fn test_list_broken_link_ok() {
|
|||
.stderr(predicate::str::contains(matched).not());
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn test_dereference_link_right_type_and_no_link() {
|
||||
let dir = tempdir();
|
||||
dir.child("target").touch().unwrap();
|
||||
let link = dir.path().join("link");
|
||||
let file_type = ".rw";
|
||||
let link_icon = "⇒";
|
||||
fs::symlink("target", &link).unwrap();
|
||||
|
||||
cmd()
|
||||
.arg("-l")
|
||||
.arg("--dereference")
|
||||
.arg(&link)
|
||||
.assert()
|
||||
.stdout(predicate::str::starts_with(file_type))
|
||||
.stdout(predicate::str::contains(link_icon).not());
|
||||
|
||||
cmd()
|
||||
.arg("-l")
|
||||
.arg("-L")
|
||||
.arg(link)
|
||||
.assert()
|
||||
.stdout(predicate::str::starts_with(file_type))
|
||||
.stdout(predicate::str::contains(link_icon).not());
|
||||
}
|
||||
|
||||
fn cmd() -> Command {
|
||||
Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue