/
githubmirror
/
fd
Обзор
Документация
Войти
/
githubmirror
/
fd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/dir_entry.rs
189 строк
5 KB
curious-rabbit
optimize
05 май 2026, 22:27
05 май 2026, 22:27
a00259b
Код
Авторство
О чём код?
use std::cell::OnceCell; use std::ffi::OsString; use std::fs::{FileType, Metadata}; use std::path::{Path, PathBuf}; use lscolors::{Colorable, LsColors, Style}; use crate::config::Config; use crate::filesystem::strip_current_dir; #[derive(Debug)] enum DirEntryInner { Normal(ignore::DirEntry), BrokenSymlink(PathBuf), } #[derive(Debug)] pub struct DirEntry { inner: DirEntryInner, metadata: OnceCell<Option<Metadata>>, style: OnceCell<Option<Style>>, } impl DirEntry { #[inline] pub fn normal(e: ignore::DirEntry) -> Self { Self { inner: DirEntryInner::Normal(e), metadata: OnceCell::new(), style: OnceCell::new(), } } pub fn broken_symlink(path: PathBuf) -> Self { Self { inner: DirEntryInner::BrokenSymlink(path), metadata: OnceCell::new(), style: OnceCell::new(), } } pub fn path(&self) -> &Path { match &self.inner { DirEntryInner::Normal(e) => e.path(), DirEntryInner::BrokenSymlink(pathbuf) => pathbuf.as_path(), } } pub fn into_path(self) -> PathBuf { match self.inner { DirEntryInner::Normal(e) => e.into_path(), DirEntryInner::BrokenSymlink(p) => p, } } /// Returns the path as it should be presented to the user. /// When stripping `./` would leave the path starting with `-`, keep the original /// (with `./`) so downstream tools don't interpret it as an option. pub fn stripped_path(&self, config: &Config) -> &Path { let path = self.path(); if config.strip_cwd_prefix { let stripped = strip_current_dir(path); if starts_with_dash(stripped) { path } else { stripped } } else { path } } /// Returns the path as it should be presented to the user. pub fn into_stripped_path(self, config: &Config) -> PathBuf { if config.strip_cwd_prefix { self.stripped_path(config).to_path_buf() } else { self.into_path() } } pub fn file_type(&self) -> Option<FileType> { match &self.inner { DirEntryInner::Normal(e) => e.file_type(), DirEntryInner::BrokenSymlink(_) => self.metadata().map(|m| m.file_type()), } } pub fn metadata(&self) -> Option<&Metadata> { self.metadata .get_or_init(|| match &self.inner { DirEntryInner::Normal(e) => e.metadata().ok(), DirEntryInner::BrokenSymlink(path) => path.symlink_metadata().ok(), }) .as_ref() } pub fn depth(&self) -> Option<usize> { match &self.inner { DirEntryInner::Normal(e) => Some(e.depth()), DirEntryInner::BrokenSymlink(_) => None, } } pub fn style(&self, ls_colors: &LsColors) -> Option<&Style> { self.style .get_or_init(|| ls_colors.style_for(self).cloned()) .as_ref() } } fn starts_with_dash(path: &Path) -> bool { path.as_os_str().as_encoded_bytes().first() == Some(&b'-') } impl PartialEq for DirEntry { #[inline] fn eq(&self, other: &Self) -> bool { self.path() == other.path() } } impl Eq for DirEntry {} impl PartialOrd for DirEntry { #[inline] fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) } } impl Ord for DirEntry { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.path().cmp(other.path()) } } impl Colorable for DirEntry { fn path(&self) -> PathBuf { self.path().to_owned() } fn file_name(&self) -> OsString { let name = match &self.inner { DirEntryInner::Normal(e) => e.file_name(), DirEntryInner::BrokenSymlink(path) => { // Path::file_name() only works if the last component is Normal, // but we want it for all component types, so we open code it. // Copied from LsColors::style_for_path_with_metadata(). path.components() .next_back() .map(|c| c.as_os_str()) .unwrap_or_else(|| path.as_os_str()) } }; name.to_owned() } fn file_type(&self) -> Option<FileType> { self.file_type() } fn metadata(&self) -> Option<Metadata> { self.metadata().cloned() } } #[cfg(test)] mod tests { use super::starts_with_dash; use std::path::Path; #[test] fn dash_prefixed_paths_detected() { assert!(starts_with_dash(Path::new("-rf"))); assert!(starts_with_dash(Path::new("--delete"))); assert!(starts_with_dash(Path::new("-"))); } #[test] fn safe_paths_not_flagged() { assert!(!starts_with_dash(Path::new("foo"))); assert!(!starts_with_dash(Path::new("./foo"))); assert!(!starts_with_dash(Path::new("sub/-rf"))); assert!(!starts_with_dash(Path::new(""))); assert!(!starts_with_dash(Path::new(" -rf"))); } }