1use f00_core::{Entry, IndicatorStyle};
2
3use crate::color::Colorizer;
4use crate::icons::icon_prefix;
5use crate::perms::classify_suffix;
6
7pub fn format_tree(
11 entries: &[Entry],
12 colorizer: &Colorizer,
13 icons: bool,
14 indicator: IndicatorStyle,
15) -> String {
16 let items: Vec<&Entry> = entries.iter().filter(|e| !e.is_dir_header).collect();
18 if items.is_empty() {
19 return String::new();
20 }
21
22 let use_depth = items.iter().any(|e| e.depth > 0);
24
25 let mut out = String::new();
26 if use_depth {
27 format_tree_by_depth(&items, colorizer, icons, indicator, &mut out);
28 } else {
29 for (i, entry) in items.iter().enumerate() {
31 let last = i + 1 == items.len();
32 let branch = if last { "└── " } else { "├── " };
33 let icon = icon_prefix(entry, icons);
34 let suffix = classify_suffix(entry, indicator);
35 let plain = format!("{icon}{}{suffix}", entry.name);
36 let name = colorizer.paint_name(entry, &plain);
37 out.push_str(branch);
38 out.push_str(&name);
39 out.push('\n');
40 }
41 }
42 out
43}
44
45fn format_tree_by_depth(
46 items: &[&Entry],
47 colorizer: &Colorizer,
48 icons: bool,
49 indicator: IndicatorStyle,
50 out: &mut String,
51) {
52 for (i, entry) in items.iter().enumerate() {
56 let depth = entry.depth.max(1); let level = depth - 1;
58
59 let is_last = is_last_sibling(items, i);
61
62 for d in 0..level {
64 let ancestor_has_more = ancestor_continues(items, i, d + 1);
66 if ancestor_has_more {
67 out.push_str("│ ");
68 } else {
69 out.push_str(" ");
70 }
71 }
72
73 out.push_str(if is_last { "└── " } else { "├── " });
74
75 let icon = icon_prefix(entry, icons);
76 let suffix = classify_suffix(entry, indicator);
77 let plain = format!("{icon}{}{suffix}", entry.name);
78 let name = colorizer.paint_name(entry, &plain);
79 out.push_str(&name);
80 out.push('\n');
81 }
82}
83
84fn is_last_sibling(items: &[&Entry], index: usize) -> bool {
85 let depth = items[index].depth;
86 let parent = items[index].path.parent().map(|p| p.to_path_buf());
87 for next in items.iter().skip(index + 1) {
88 if next.depth < depth {
89 return true;
90 }
91 if next.depth == depth {
92 let next_parent = next.path.parent().map(|p| p.to_path_buf());
93 if next_parent == parent {
94 return false;
95 }
96 if next.depth == depth {
98 return true;
99 }
100 }
101 }
102 true
103}
104
105fn ancestor_continues(items: &[&Entry], index: usize, ancestor_depth: usize) -> bool {
106 let path = &items[index].path;
109 let ancestors: Vec<_> = path.ancestors().collect();
110 for next in items.iter().skip(index + 1) {
115 if next.depth < ancestor_depth {
116 return false;
117 }
118 if next.depth == ancestor_depth {
119 return true;
120 }
121 }
122 let _ = ancestors;
123 false
124}