@@ -599,20 +599,123 @@ pub fn info(path: &Path) -> Result<()> {
599599 let df = MftReader :: load_parquet ( path)
600600 . with_context ( || format ! ( "Failed to load index: {}" , path. display( ) ) ) ?;
601601
602- let mut stdout = std:: io:: stdout ( ) . lock ( ) ;
603- writeln ! ( stdout, "Index: {}" , path. display( ) ) ?;
604- writeln ! ( stdout, "Records: {}" , df. height( ) ) ?;
605- writeln ! ( stdout, "Columns: {}" , df. width( ) ) ?;
606- writeln ! ( stdout) ?;
607- writeln ! ( stdout, "Schema:" ) ?;
602+ // Get absolute path and file size
603+ let abs_path = std:: fs:: canonicalize ( path) . unwrap_or_else ( |_| path. to_path_buf ( ) ) ;
604+ let file_size = std:: fs:: metadata ( path) . map ( |m| m. len ( ) ) . unwrap_or ( 0 ) ;
605+
606+ let total_records = df. height ( ) ;
607+
608+ // Extract statistics from the DataFrame
609+ let dir_count = df
610+ . column ( "is_directory" )
611+ . ok ( )
612+ . and_then ( |c| c. bool ( ) . ok ( ) )
613+ . map ( |b| b. sum ( ) . unwrap_or ( 0 ) as u64 )
614+ . unwrap_or ( 0 ) ;
615+ let file_count = ( total_records as u64 ) . saturating_sub ( dir_count) ;
616+
617+ // Helper closure to count bool columns
618+ let count_bool = |name : & str | -> u64 {
619+ df. column ( name)
620+ . ok ( )
621+ . and_then ( |c| c. bool ( ) . ok ( ) )
622+ . map ( |b| b. sum ( ) . unwrap_or ( 0 ) as u64 )
623+ . unwrap_or ( 0 )
624+ } ;
625+
626+ let hidden_count = count_bool ( "is_hidden" ) ;
627+ let system_count = count_bool ( "is_system" ) ;
628+ let compressed_count = count_bool ( "is_compressed" ) ;
629+ let encrypted_count = count_bool ( "is_encrypted" ) ;
630+ let sparse_count = count_bool ( "is_sparse" ) ;
631+ let reparse_count = count_bool ( "is_reparse" ) ;
632+ let readonly_count = count_bool ( "is_readonly" ) ;
633+ let archive_count = count_bool ( "is_archive" ) ;
634+
635+ // Total size calculation
636+ let total_size: u64 = df
637+ . column ( "size" )
638+ . ok ( )
639+ . and_then ( |c| c. u64 ( ) . ok ( ) )
640+ . map ( |s| s. iter ( ) . flatten ( ) . sum ( ) )
641+ . unwrap_or ( 0 ) ;
642+
643+ // Allocated size calculation
644+ let total_allocated: u64 = df
645+ . column ( "allocated_size" )
646+ . ok ( )
647+ . and_then ( |c| c. u64 ( ) . ok ( ) )
648+ . map ( |s| s. iter ( ) . flatten ( ) . sum ( ) )
649+ . unwrap_or ( 0 ) ;
650+
651+ // Count multi-stream and multi-name files
652+ let multi_stream_count = df
653+ . column ( "stream_count" )
654+ . ok ( )
655+ . and_then ( |c| c. u16 ( ) . ok ( ) )
656+ . map ( |s| s. iter ( ) . filter ( |v| v. is_some_and ( |x| x > 1 ) ) . count ( ) as u64 )
657+ . unwrap_or ( 0 ) ;
658+ let multi_name_count = df
659+ . column ( "name_count" )
660+ . ok ( )
661+ . and_then ( |c| c. u16 ( ) . ok ( ) )
662+ . map ( |s| s. iter ( ) . filter ( |v| v. is_some_and ( |x| x > 1 ) ) . count ( ) as u64 )
663+ . unwrap_or ( 0 ) ;
664+
665+ println ! ( "═══════════════════════════════════════════════════════════════" ) ;
666+ println ! ( " INDEX FILE INFO" ) ;
667+ println ! ( "═══════════════════════════════════════════════════════════════" ) ;
668+ println ! ( ) ;
669+ println ! ( "📁 FILE DETAILS" ) ;
670+ println ! ( " Path: {}" , abs_path. display( ) ) ;
671+ println ! ( " File size: {}" , format_size( file_size) ) ;
672+ println ! ( " Columns: {}" , df. width( ) ) ;
673+ println ! ( ) ;
674+ println ! ( "📊 RECORD STATISTICS" ) ;
675+ println ! ( " Total records: {}" , format_number( total_records as u64 ) ) ;
676+ println ! ( " Directories: {}" , format_number( dir_count) ) ;
677+ println ! ( " Files: {}" , format_number( file_count) ) ;
678+ println ! ( ) ;
679+ println ! ( "💾 SIZE METRICS" ) ;
680+ println ! ( " Total file size: {}" , format_size( total_size) ) ;
681+ println ! ( " Total allocated: {}" , format_size( total_allocated) ) ;
682+ println ! ( ) ;
683+ println ! ( "🏷️ ATTRIBUTES" ) ;
684+ println ! ( " Hidden: {}" , format_number( hidden_count) ) ;
685+ println ! ( " System: {}" , format_number( system_count) ) ;
686+ println ! ( " Read-only: {}" , format_number( readonly_count) ) ;
687+ println ! ( " Archive: {}" , format_number( archive_count) ) ;
688+ println ! ( " Compressed: {}" , format_number( compressed_count) ) ;
689+ println ! ( " Encrypted: {}" , format_number( encrypted_count) ) ;
690+ println ! ( " Sparse: {}" , format_number( sparse_count) ) ;
691+ println ! ( " Reparse points: {}" , format_number( reparse_count) ) ;
692+ println ! ( ) ;
693+ println ! ( "🔗 ADVANCED" ) ;
694+ println ! ( " Multi-stream files: {}" , format_number( multi_stream_count) ) ;
695+ println ! ( " Multi-name files: {}" , format_number( multi_name_count) ) ;
696+ println ! ( ) ;
697+ println ! ( "📋 SCHEMA" ) ;
608698 let schema = df. schema ( ) ;
609699 for ( name, dtype) in schema. iter ( ) {
610- writeln ! ( stdout , " {name}: {dtype}" ) ? ;
700+ println ! ( " {name}: {dtype}" ) ;
611701 }
612702
613703 Ok ( ( ) )
614704}
615705
706+ /// Format a number with comma separators.
707+ fn format_number ( n : u64 ) -> String {
708+ let s = n. to_string ( ) ;
709+ let mut result = String :: with_capacity ( s. len ( ) + s. len ( ) / 3 ) ;
710+ for ( i, c) in s. chars ( ) . rev ( ) . enumerate ( ) {
711+ if i > 0 && i % 3 == 0 {
712+ result. push ( ',' ) ;
713+ }
714+ result. push ( c) ;
715+ }
716+ result. chars ( ) . rev ( ) . collect ( )
717+ }
718+
616719/// Format file size in human-readable format.
617720#[ allow( clippy:: cast_precision_loss, clippy:: float_arithmetic) ]
618721fn format_size ( bytes : u64 ) -> String {
0 commit comments