From ea649535523f861b17d7a2636405ea28ac6e6605 Mon Sep 17 00:00:00 2001 From: Dan Sully Date: Fri, 8 Oct 2021 11:12:57 -0700 Subject: [PATCH] Extract a symlink's target type if possible, so it can have proper colors. --- CHANGELOG.md | 1 + src/meta/symlink.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e19c2d2..1a52d541f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate ### Added +- Added support for coloring symlink targets. - Added support for the MISSING / mi= dircolors variable for broken symlink targets. - Add support for theme from [zwpaper](https://github.com/zwpaper) [#452](https://github.com/Peltoche/lsd/pull/452) - Update minimal rust version to 1.42.0 from [zwpaper](https://github.com/zwpaper) [#534](https://github.com/Peltoche/lsd/issues/534) diff --git a/src/meta/symlink.rs b/src/meta/symlink.rs index bb9437c04..3606fcc4c 100644 --- a/src/meta/symlink.rs +++ b/src/meta/symlink.rs @@ -1,17 +1,29 @@ use crate::color::{ColoredString, Colors, Elem}; use crate::flags::Flags; +use crate::meta::{FileType, Permissions}; use std::fs::read_link; use std::path::Path; #[derive(Clone, Debug)] pub struct SymLink { target: Option, + target_type: Option, valid: bool, } impl<'a> From<&'a Path> for SymLink { fn from(path: &'a Path) -> Self { if let Ok(target) = read_link(path) { + // Extract the symlink's target type if possible, so it can have proper colors. + let target_type = match target.metadata() { + Ok(metadata) => Some(FileType::new( + &metadata, + None, + &Permissions::from(&metadata), + )), + Err(_) => None, + }; + if target.is_absolute() || path.parent() == None { return Self { valid: target.exists(), @@ -21,6 +33,7 @@ impl<'a> From<&'a Path> for SymLink { .expect("failed to convert symlink to str") .to_string(), ), + target_type, }; } @@ -31,12 +44,14 @@ impl<'a> From<&'a Path> for SymLink { .expect("failed to convert symlink to str") .to_string(), ), + target_type, valid: path.parent().unwrap().join(target).exists(), }; } Self { target: None, + target_type: None, valid: false, } } @@ -50,7 +65,20 @@ impl SymLink { pub fn render(&self, colors: &Colors, flag: &Flags) -> ColoredString { if let Some(target_string) = self.symlink_string() { let elem = if self.valid { - &Elem::SymLink + // Proper colors for symlink target file types. + match self.target_type { + Some(FileType::BlockDevice) => &Elem::BlockDevice, + Some(FileType::CharDevice) => &Elem::CharDevice, + Some(FileType::Directory { uid: _ }) => &Elem::Dir { uid: false }, + Some(FileType::File { uid: _, exec: _ }) => &Elem::File { + uid: false, + exec: false, + }, + Some(FileType::Pipe) => &Elem::Pipe, + Some(FileType::Socket) => &Elem::Socket, + Some(FileType::Special) => &Elem::Special, + _ => &Elem::SymLink, + } } else { &Elem::MissingSymLinkTarget }; @@ -79,11 +107,13 @@ mod tests { use crate::color::{Colors, ThemeOption}; use crate::config_file::Config; use crate::flags::Flags; + use crate::meta::FileType; #[test] fn test_symlink_render_default_valid_target_nocolor() { let link = SymLink { target: Some("/target".to_string()), + target_type: None, valid: true, }; let argv = vec!["lsd"]; @@ -102,6 +132,7 @@ mod tests { fn test_symlink_render_default_invalid_target_nocolor() { let link = SymLink { target: Some("/target".to_string()), + target_type: None, valid: false, }; let argv = vec!["lsd"]; @@ -120,6 +151,7 @@ mod tests { fn test_symlink_render_default_invalid_target_withcolor() { let link = SymLink { target: Some("/target".to_string()), + target_type: Some(FileType::SymLink { is_dir: false }), valid: false, }; let argv = vec!["lsd"];