@@ -5,25 +5,74 @@ use crate::age;
55use crate :: file_info;
66use crate :: utils;
77
8+ fn truncate_to_width ( s : & str , max_width : usize ) -> String {
9+ if s. chars ( ) . count ( ) <= max_width {
10+ s. to_string ( )
11+ } else if max_width <= 3 {
12+ "." . repeat ( max_width)
13+ } else {
14+ let truncated: String = s. chars ( ) . take ( max_width - 3 ) . collect ( ) ;
15+ format ! ( "{}..." , truncated)
16+ }
17+ }
18+
19+ // Calculate display width accounting for emojis (4 bytes but 2 display chars)
20+ fn display_width ( s : & str ) -> usize {
21+ let mut width = 0 ;
22+ for c in s. chars ( ) {
23+ if c. len_utf8 ( ) == 4 {
24+ width += 2 ; // emoji - 2 display chars
25+ } else {
26+ width += 1 ;
27+ }
28+ }
29+ width
30+ }
31+
32+ fn pad_to_width ( s : & str , target_width : usize ) -> String {
33+ let current_width = display_width ( s) ;
34+ if current_width >= target_width {
35+ s. to_string ( )
36+ } else {
37+ format ! ( "{}{}" , s, " " . repeat( target_width - current_width) )
38+ }
39+ }
40+
841pub fn print_hero (
942 td : & str ,
1043 dstats : & age:: AgeStats ,
1144 top_file_by_size : ( & String , u64 ) ,
1245 top_n_percentage : f64 ,
1346) {
1447 let width = 90 ;
15- let title = format ! ( "dirpulse · {}" , td) ;
48+ let inner_width = width - 2 ; // space for "│ " and " │"
49+
50+ // Truncate title if needed
51+ let title = truncate_to_width ( & format ! ( "dirpulse · {}" , td) , inner_width) ;
52+
53+ // Calculate available space for filename in stats line
54+ // Format: "🔴 {stale} Stale, 📦 Largest: {name} ({size}), 📊 Top 10 = {pct}%"
55+ let stale_str = utils:: bytes_to_human ( dstats. stale . size ) ;
56+ let size_str = utils:: bytes_to_human ( top_file_by_size. 1 ) ;
57+ let pct_str = format ! ( "{:.2}%" , top_n_percentage) ;
58+
59+ // Calculate fixed parts display width
60+ // "🔴 " (4) + stale + " Stale, 📦 Largest: " (20) + name + " (" (2) + size + "), 📊 Top 10 = " (16) + pct
61+ // Each emoji is 2 display chars, so: 🔴=2, 📦=2, 📊=2
62+ let fixed_display_width = 4 + stale_str. len ( ) + 20 + 2 + size_str. len ( ) + 16 + pct_str. len ( ) ;
63+ let max_name_len = inner_width. saturating_sub ( fixed_display_width) ;
64+
65+ let truncated_name = truncate_to_width ( top_file_by_size. 0 , max_name_len) ;
66+
1667 let stats = format ! (
17- "🔴 {:<5} Stale, 📦 Largest: {} ({}), 📊 Top 10 = {:.2}%" ,
18- utils:: bytes_to_human( dstats. stale. size) ,
19- top_file_by_size. 0 ,
20- utils:: bytes_to_human( top_file_by_size. 1 ) ,
21- top_n_percentage
68+ "🔴 {} Stale, 📦 Largest: {} ({}), 📊 Top 10 = {}" ,
69+ stale_str, truncated_name, size_str, pct_str
2270 ) ;
71+
2372 println ! ( "┌{}┐" , "─" . repeat( width) ) ;
24- println ! ( "│ {:<w$ } │" , title, w = width - 2 ) ;
73+ println ! ( "│ {} │" , pad_to_width ( & title, inner_width ) ) ;
2574 println ! ( "├{}┤" , "─" . repeat( width) ) ;
26- println ! ( "│ {:<w$ } │" , stats, w = width - 5 ) ;
75+ println ! ( "│ {} │" , pad_to_width ( & stats, inner_width ) ) ;
2776 println ! ( "└{}┘" , "─" . repeat( width) ) ;
2877}
2978
@@ -38,14 +87,14 @@ pub fn print_stat_line(file_count: u64, dir_count: u64, total_size: u64) {
3887
3988pub fn print_top_largest ( top_size : u64 , sorted_heap : & Vec < Reverse < file_info:: FileInfo > > ) {
4089 println ! (
41- "── Top {} Largest ────────────────────────────────────────────────────" ,
90+ "── Top {} Largest ─────────────────────────────────────────────────────── " ,
4291 top_size
4392 ) ;
4493
4594 let mut count = 1 ;
4695 for entry in sorted_heap {
4796 println ! (
48- "{:<10} {:<7 } {:<40}" ,
97+ "{:<10} {:<10 } {:<40}" ,
4998 count,
5099 utils:: bytes_to_human( entry. 0 . size) ,
51100 entry
@@ -59,9 +108,12 @@ pub fn print_top_largest(top_size: u64, sorted_heap: &Vec<Reverse<file_info::Fil
59108 }
60109}
61110
62- pub fn print_extensions ( file_types : & HashMap < String , file_info:: TypeStats > ) {
63- println ! ( "── By Extension ──────────────────────────────────────────────────────" ) ;
64- let mut sorted_exts: Vec < _ > = file_types. iter ( ) . collect ( ) ;
111+ pub fn print_extensions ( file_types : & HashMap < String , file_info:: TypeStats > , top_extensions : u64 ) {
112+ println ! (
113+ "── Top {} Extension ─────────────────────────────────────────────────────" ,
114+ top_extensions
115+ ) ;
116+ let mut sorted_exts: Vec < _ > = file_types. iter ( ) . take ( top_extensions as usize ) . collect ( ) ;
65117 sorted_exts. sort_by ( |a, b| b. 1 . file_count . cmp ( & a. 1 . file_count ) ) ;
66118 for ( key, value) in sorted_exts {
67119 println ! (
@@ -74,7 +126,7 @@ pub fn print_extensions(file_types: &HashMap<String, file_info::TypeStats>) {
74126}
75127
76128pub fn print_file_age ( file_age : & age:: AgeStats ) {
77- println ! ( "── File Age ──────────────────────────────────────────────────────────" ) ;
129+ println ! ( "── File Age ────────────────────────────────────────────────────────────── " ) ;
78130 println ! (
79131 "{:<30} {:>5} files {:>10} " ,
80132 "🟢 Fresh (< 30 days)" ,
0 commit comments