1use crate::config::Config;
2use crate::file_entry::{FileEntry, FileType};
3use crate::sort::sort_default;
4use colored::Colorize;
5
6pub fn format_long(mut entries: Vec<FileEntry>, config: &Config) {
7 sort_default(&mut entries);
9
10 print_long_entries(&entries, config, "");
11}
12
13pub fn print_long_entries(entries: &[FileEntry], config: &Config, prefix: &str) {
14 if entries.is_empty() {
15 return;
16 }
17
18 let fields = &config.display.long_format_fields;
19 let widths = calculate_column_widths(entries, fields);
20 print_long_entries_with_widths(entries, config, prefix, fields, &widths);
21}
22
23pub fn calculate_column_widths(
24 entries: &[FileEntry],
25 fields: &[String],
26) -> std::collections::HashMap<String, usize> {
27 let mut max_widths: std::collections::HashMap<String, usize> = std::collections::HashMap::new();
28
29 for field in fields {
30 let width = match field.as_str() {
31 "nlink" => entries
32 .iter()
33 .map(|e| e.nlink.to_string().len())
34 .max()
35 .unwrap_or(0),
36 "owner" => entries.iter().map(|e| e.owner.len()).max().unwrap_or(0),
37 "group" => entries.iter().map(|e| e.group.len()).max().unwrap_or(0),
38 "size" => entries
39 .iter()
40 .map(|e| e.format_size().len())
41 .max()
42 .unwrap_or(0),
43 "filename" => entries
44 .iter()
45 .map(|e| e.path.to_string_lossy().len())
46 .max()
47 .unwrap_or(0),
48 "permissions" => entries
49 .iter()
50 .map(|e| e.format_permissions().len())
51 .max()
52 .unwrap_or(0),
53 _ => 0,
54 };
55 max_widths.insert(field.clone(), width);
56 }
57
58 max_widths
59}
60
61pub fn print_long_entries_with_widths(
62 entries: &[FileEntry],
63 config: &Config,
64 prefix: &str,
65 fields: &[String],
66 widths: &std::collections::HashMap<String, usize>,
67) {
68 for entry in entries {
70 let mut output_parts: Vec<String> = Vec::new();
71
72 for (idx, field) in fields.iter().enumerate() {
73 let part = match field.as_str() {
74 "permissions" => entry.format_permissions(),
75 "nlink" => {
76 let width = widths.get("nlink").copied().unwrap_or(0);
77 format!("{:>width$}", entry.nlink.to_string(), width = width)
78 }
79 "owner" => {
80 let width = widths.get("owner").copied().unwrap_or(0);
81 format!("{:<width$}", entry.owner, width = width)
82 }
83 "group" => {
84 let width = widths.get("group").copied().unwrap_or(0);
85 format!("{:<width$}", entry.group, width = width)
86 }
87 "size" => {
88 let width = widths.get("size").copied().unwrap_or(0);
89 format!("{:>width$}", entry.format_size(), width = width)
90 }
91 "modified" => entry.format_modified(),
92 "icon" => {
93 let icon = entry.get_icon_custom(&config.icons);
94 let icon_color = entry.get_icon_color(&config.icons.colors);
95 format!("{}", icon.color(icon_color))
96 }
97 "filename" => {
98 let filename_str = entry.path.to_string_lossy().to_string();
99 let width = widths.get("filename").copied().unwrap_or(0);
100
101 let padded = if idx < fields.len() - 1 {
103 format!("{:<width$}", filename_str, width = width)
104 } else {
105 filename_str
106 };
107
108 let filename_colored = match entry.get_file_type() {
109 FileType::Directory | FileType::Executable => {
110 padded.color(entry.get_color(&config.colors)).bold()
111 }
112 FileType::RegularFile => padded.color(entry.get_color(&config.colors)),
113 };
114 format!("{}", filename_colored)
115 }
116 _ => String::new(),
117 };
118 output_parts.push(part);
119 }
120
121 println!("{}{}", prefix, output_parts.join(" "));
122 }
123}