use { super::*, crate::{ app::{Selection, SelectionType}, file_sum::FileSum, git::LineGitStatus, tree_build::BId, }, lazy_regex::regex_captures, std::{ fs, path::{Path, PathBuf}, }, }; #[cfg(unix)] use {std::os::unix::fs::MetadataExt, umask::Mode}; #[cfg(windows)] use is_executable::IsExecutable; /// a line in the representation of the file hierarchy #[derive(Debug, Clone)] pub struct TreeLine { pub bid: BId, pub parent_bid: Option, pub left_branchs: Box<[bool]>, // a depth-sized array telling whether a branch pass pub depth: u16, pub path: PathBuf, pub subpath: String, pub icon: Option, pub name: String, // a displayable name - some chars may have been stripped pub line_type: TreeLineType, pub has_error: bool, pub nb_kept_children: usize, pub unlisted: usize, // number of not listed children (Dir) or brothers (Pruning) pub score: i32, // 0 if there's no pattern pub direct_match: bool, pub sum: Option, // None when not measured pub metadata: fs::Metadata, pub git_status: Option, } impl TreeLine { pub fn double_extension_from_name(name: &str) -> Option<&str> { regex_captures!(r"\.([^.]+\.[^.]+)", name) .map(|(_, de)| de) } pub fn extension_from_name(name: &str) -> Option<&str> { regex_captures!(r"\.([^.]+)$", name) .map(|(_, ext)| ext) } pub fn is_selectable(&self) -> bool { !matches!(&self.line_type, TreeLineType::Pruning) } pub fn is_dir(&self) -> bool { match &self.line_type { TreeLineType::Dir => true, TreeLineType::SymLink { final_is_dir, .. } if *final_is_dir => true, _ => false, } } pub fn is_file(&self) -> bool { matches!(&self.line_type, TreeLineType::File) } pub fn is_of(&self, selection_type: SelectionType) -> bool { match selection_type { SelectionType::Any => true, SelectionType::File => self.is_file(), SelectionType::Directory => self.is_dir(), } } pub fn extension(&self) -> Option<&str> { Self::extension_from_name(&self.name) } pub fn selection_type(&self) -> SelectionType { use TreeLineType::*; match &self.line_type { File => SelectionType::File, Dir | BrokenSymLink(_) => SelectionType::Directory, SymLink { final_is_dir, .. } => { if *final_is_dir { SelectionType::Directory } else { SelectionType::File } } Pruning => SelectionType::Any, // should not happen today } } pub fn as_selection(&self) -> Selection<'_> { Selection { path: &self.path, stype: self.selection_type(), is_exe: self.is_exe(), line: 0, } } #[cfg(unix)] pub fn mode(&self) -> Mode { Mode::from(self.metadata.mode()) } #[cfg(unix)] pub fn device_id(&self) -> lfs_core::DeviceId { self.metadata.dev().into() } #[cfg(unix)] pub fn mount(&self) -> Option { use crate::filesystems::*; let mut mount_list = MOUNTS.lock().unwrap(); if mount_list.load().is_ok() { mount_list .get_by_device_id(self.metadata.dev().into()) .cloned() } else { None } } pub fn is_exe(&self) -> bool { #[cfg(unix)] return self.mode().is_exe(); #[cfg(windows)] return self.path.is_executable(); } /// build and return the absolute targeted path: either self.path or the /// solved canonicalized symlink pub fn target(&self) -> &Path { match &self.line_type { TreeLineType::SymLink { final_target, .. } => final_target, _ => &self.path, } } }