lx_cli/
file_entry.rs

1/// Core data structures (FileEntry and FileType) that represent files and its metadata.
2use crate::config::{ColorConfig, IconColorConfig, IconConfig};
3use crate::icon::FileIcon;
4use colored::Color;
5use std::ffi::OsString;
6use std::time::SystemTime;
7
8#[derive(Debug, Clone)]
9pub struct FileEntry {
10    pub path: OsString,
11    pub is_dir: bool,
12    pub is_executable: bool,
13    pub mode: u32,
14    pub size: u64,
15    pub modified: SystemTime,
16    pub owner: String,
17    pub group: String,
18    pub nlink: u64,
19}
20
21impl FileEntry {
22    fn get_file_icon(&self) -> FileIcon {
23        if self.is_dir {
24            FileIcon::Directory
25        } else if self.is_executable {
26            FileIcon::Executable
27        } else {
28            FileIcon::RegularFile
29        }
30    }
31
32    pub fn get_icon(&self) -> String {
33        self.get_file_icon().as_str().to_string()
34    }
35
36    pub fn get_icon_custom(&self, config: &IconConfig) -> String {
37        self.get_file_icon().as_str_custom(config)
38    }
39
40    pub fn get_color(&self, config: &ColorConfig) -> Color {
41        self.get_file_icon().get_color(config)
42    }
43
44    pub fn get_icon_color(&self, config: &IconColorConfig) -> Color {
45        self.get_file_icon().get_icon_color(config)
46    }
47
48    pub fn get_file_type(&self) -> FileType {
49        if self.is_dir {
50            FileType::Directory
51        } else if self.is_executable {
52            FileType::Executable
53        } else {
54            FileType::RegularFile
55        }
56    }
57
58    pub fn format_permissions(&self) -> String {
59        let mode = self.mode;
60
61        // File type
62        let file_type = if self.is_dir { 'd' } else { '-' };
63
64        // Owner permissions
65        let user_r = if mode & 0o400 != 0 { 'r' } else { '-' };
66        let user_w = if mode & 0o200 != 0 { 'w' } else { '-' };
67        let user_x = if mode & 0o100 != 0 { 'x' } else { '-' };
68
69        // Group permissions
70        let group_r = if mode & 0o040 != 0 { 'r' } else { '-' };
71        let group_w = if mode & 0o020 != 0 { 'w' } else { '-' };
72        let group_x = if mode & 0o010 != 0 { 'x' } else { '-' };
73
74        // Other permissions
75        let other_r = if mode & 0o004 != 0 { 'r' } else { '-' };
76        let other_w = if mode & 0o002 != 0 { 'w' } else { '-' };
77        let other_x = if mode & 0o001 != 0 { 'x' } else { '-' };
78
79        format!(
80            "{}{}{}{}{}{}{}{}{}{}",
81            file_type, user_r, user_w, user_x, group_r, group_w, group_x, other_r, other_w, other_x
82        )
83    }
84
85    pub fn format_size(&self) -> String {
86        let size = self.size;
87        if size < 1024 {
88            format!("{}B", size)
89        } else if size < 1024 * 1024 {
90            format!("{:.1}K", size as f64 / 1024.0)
91        } else if size < 1024 * 1024 * 1024 {
92            format!("{:.1}M", size as f64 / (1024.0 * 1024.0))
93        } else {
94            format!("{:.1}G", size as f64 / (1024.0 * 1024.0 * 1024.0))
95        }
96    }
97
98    pub fn format_modified(&self) -> String {
99        use std::time::UNIX_EPOCH;
100
101        if let Ok(duration) = self.modified.duration_since(UNIX_EPOCH) {
102            let secs = duration.as_secs();
103            let datetime = chrono::DateTime::from_timestamp(secs as i64, 0)
104                .unwrap_or_else(|| chrono::DateTime::from_timestamp(0, 0).unwrap());
105            datetime.format("%Y-%m-%d %H:%M:%S").to_string()
106        } else {
107            "Unknown".to_string()
108        }
109    }
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
113pub enum FileType {
114    Directory,
115    Executable,
116    RegularFile,
117}