@@ -2,8 +2,7 @@ use std::io::{self, Write};
22
33use crate :: cache:: { build_cache, load_cache, store_cache, sync_cache} ;
44use crate :: common:: { find_files, get_repo_hash} ;
5- use crate :: parse:: parse_repo;
6- use crate :: types:: { CacheEncoding , CodeownersCache , CodeownersEntry , OutputFormat } ;
5+ use crate :: types:: { CacheEncoding , CodeownersEntry , OutputFormat } ;
76
87use utils:: app_config:: AppConfig ;
98use utils:: error:: Result ;
@@ -22,10 +21,6 @@ pub fn codeowners_parse(
2221) -> Result < ( ) > {
2322 println ! ( "Parsing CODEOWNERS files at {}" , path. display( ) ) ;
2423
25- let hash = get_repo_hash ( path. as_ref ( ) ) ?;
26- dbg ! ( hash) ;
27- panic ! ( ) ;
28-
2924 let cache_file = match cache_file {
3025 Some ( file) => path. join ( file) ,
3126 None => {
@@ -117,40 +112,107 @@ pub fn codeowners_list_files(
117112 // Output the filtered files in the requested format
118113 match format {
119114 OutputFormat :: Text => {
120- for file in filtered_files {
121- let owners_str = file
122- . owners
123- . iter ( )
124- . map ( |o| o. identifier . clone ( ) )
125- . collect :: < Vec < _ > > ( )
126- . join ( ", " ) ;
127-
128- let tags_str = file
129- . tags
130- . iter ( )
131- . map ( |t| t. 0 . clone ( ) )
132- . collect :: < Vec < _ > > ( )
133- . join ( ", " ) ;
134-
135- println ! ( "File: {}" , file. path. display( ) ) ;
136- println ! (
137- " Owners: {}" ,
138- if owners_str. is_empty( ) {
139- "None"
115+ // Set column widths that work better for most displays
116+ let path_width = 45 ; // Max width for path display
117+ let owner_width = 26 ; // More space for owners
118+ let tag_width = 26 ; // More space for tags
119+
120+ // Print header
121+ println ! (
122+ "==============================================================================="
123+ ) ;
124+ println ! (
125+ " {:<path_width$} {:<owner_width$} {:<tag_width$}" ,
126+ "File Path" ,
127+ "Owners" ,
128+ "Tags" ,
129+ path_width = path_width,
130+ owner_width = owner_width,
131+ tag_width = tag_width
132+ ) ;
133+ println ! (
134+ "==============================================================================="
135+ ) ;
136+
137+ // Print each file entry
138+ for file in & filtered_files {
139+ // Format the path - keep the filename but truncate the path if needed
140+ let path_str = file. path . to_string_lossy ( ) ;
141+ let path_display = if path_str. len ( ) > path_width {
142+ // Extract filename
143+ let filename = file
144+ . path
145+ . file_name ( )
146+ . map ( |f| f. to_string_lossy ( ) . to_string ( ) )
147+ . unwrap_or_default ( ) ;
148+
149+ // Calculate available space for parent path
150+ let available_space = path_width. saturating_sub ( filename. len ( ) + 4 ) ; // +4 for ".../"
151+
152+ if available_space > 5 {
153+ // Show part of the parent path
154+ let parent_path = path_str. to_string ( ) ;
155+ let start_pos = parent_path. len ( ) . saturating_sub ( path_width - 3 ) ;
156+ format ! ( "...{}" , & parent_path[ start_pos..] )
140157 } else {
141- & owners_str
158+ // Just show the filename with ellipsis
159+ format ! ( ".../{}" , filename)
142160 }
143- ) ;
161+ } else {
162+ path_str. to_string ( )
163+ } ;
164+
165+ // Format owners with more space
166+ let owners_str = if file. owners . is_empty ( ) {
167+ "None" . to_string ( )
168+ } else {
169+ file. owners
170+ . iter ( )
171+ . map ( |o| o. identifier . clone ( ) )
172+ . collect :: < Vec < _ > > ( )
173+ . join ( ", " )
174+ } ;
175+
176+ let owners_display = if owners_str. len ( ) > owner_width {
177+ format ! ( "{}..." , & owners_str[ 0 ..owner_width - 3 ] )
178+ } else {
179+ owners_str
180+ } ;
181+
182+ // Format tags with more space
183+ let tags_str = if file. tags . is_empty ( ) {
184+ "None" . to_string ( )
185+ } else {
186+ file. tags
187+ . iter ( )
188+ . map ( |t| t. 0 . clone ( ) )
189+ . collect :: < Vec < _ > > ( )
190+ . join ( ", " )
191+ } ;
192+
193+ let tags_display = if tags_str. len ( ) > tag_width {
194+ format ! ( "{}..." , & tags_str[ 0 ..tag_width - 3 ] )
195+ } else {
196+ tags_str
197+ } ;
198+
144199 println ! (
145- " Tags: {}" ,
146- if tags_str. is_empty( ) {
147- "None"
148- } else {
149- & tags_str
150- }
200+ " {:<path_width$} {:<owner_width$} {:<tag_width$}" ,
201+ path_display,
202+ owners_display,
203+ tags_display,
204+ path_width = path_width,
205+ owner_width = owner_width,
206+ tag_width = tag_width
151207 ) ;
152- println ! ( ) ;
153208 }
209+ println ! (
210+ "==============================================================================="
211+ ) ;
212+ println ! ( " Total: {} files" , filtered_files. len( ) ) ;
213+ println ! (
214+ "==============================================================================="
215+ ) ;
154216 }
155217 OutputFormat :: Json => {
156218 println ! ( "{}" , serde_json:: to_string_pretty( & filtered_files) . unwrap( ) ) ;
@@ -185,35 +247,87 @@ pub fn codeowners_list_owners(
185247 // Process the owners from the cache
186248 match format {
187249 OutputFormat :: Text => {
188- println ! ( "CODEOWNERS Ownership Report" ) ;
189- println ! ( "==========================\n " ) ;
250+ // Column widths for the table
251+ let owner_width = 35 ; // For owner identifiers
252+ let type_width = 10 ; // For owner type
253+ let count_width = 10 ; // For file count
254+ let file_width = 45 ; // For sample files
255+
256+ println ! (
257+ "==============================================================================="
258+ ) ;
259+ println ! (
260+ " {:<owner_width$} {:<type_width$} {:<count_width$} {:<file_width$}" ,
261+ "Owner" ,
262+ "Type" ,
263+ "Files" ,
264+ "Sample Files" ,
265+ owner_width = owner_width,
266+ type_width = type_width,
267+ count_width = count_width,
268+ file_width = file_width
269+ ) ;
270+ println ! (
271+ "==============================================================================="
272+ ) ;
190273
191274 if cache. owners_map . is_empty ( ) {
192- println ! ( "No owners found in the codebase." ) ;
275+ println ! ( " No owners found in the codebase." ) ;
193276 } else {
194277 // Sort owners by number of files they own (descending)
195278 let mut owners_with_counts: Vec < _ > = cache. owners_map . iter ( ) . collect ( ) ;
196279 owners_with_counts. sort_by ( |a, b| b. 1 . len ( ) . cmp ( & a. 1 . len ( ) ) ) ;
197280
198281 for ( owner, paths) in owners_with_counts {
199- println ! ( "Owner: {} ({})" , owner. identifier, owner. owner_type) ;
200- println ! ( "Files owned: {}" , paths. len( ) ) ;
201-
202- // List first 5 files (to avoid overwhelming output)
203- if !paths. is_empty ( ) {
204- println ! ( "Sample files:" ) ;
205- for path in paths. iter ( ) . take ( 5 ) {
206- println ! ( " - {}" , path. display( ) ) ;
207- }
208-
209- if paths. len ( ) > 5 {
210- println ! ( " ... and {} more" , paths. len( ) - 5 ) ;
282+ // Prepare sample file list
283+ let file_samples = if paths. is_empty ( ) {
284+ "None" . to_string ( )
285+ } else {
286+ let samples: Vec < _ > = paths
287+ . iter ( )
288+ . take ( 3 ) // Show max 3 files as samples
289+ . map ( |p| {
290+ let file_name = p
291+ . file_name ( )
292+ . map ( |f| f. to_string_lossy ( ) . to_string ( ) )
293+ . unwrap_or_else ( || p. to_string_lossy ( ) . to_string ( ) ) ;
294+ file_name
295+ } )
296+ . collect ( ) ;
297+ let mut display = samples. join ( ", " ) ;
298+ if paths. len ( ) > 3 {
299+ display. push_str ( & format ! ( " (+{})" , paths. len( ) - 3 ) ) ;
211300 }
212- }
301+ display
302+ } ;
213303
214- println ! ( ) ; // Empty line between owners
304+ // Trim the owner identifier if too long
305+ let owner_display = if owner. identifier . len ( ) > owner_width {
306+ format ! ( "{}..." , & owner. identifier[ 0 ..owner_width - 3 ] )
307+ } else {
308+ owner. identifier . clone ( )
309+ } ;
310+
311+ println ! (
312+ " {:<owner_width$} {:<type_width$} {:<count_width$} {:<file_width$}" ,
313+ owner_display,
314+ owner. owner_type,
315+ paths. len( ) ,
316+ file_samples,
317+ owner_width = owner_width,
318+ type_width = type_width,
319+ count_width = count_width,
320+ file_width = file_width
321+ ) ;
215322 }
216323 }
324+ println ! (
325+ "==============================================================================="
326+ ) ;
327+ println ! ( " Total: {} owners" , cache. owners_map. len( ) ) ;
328+ println ! (
329+ "==============================================================================="
330+ ) ;
217331 }
218332 OutputFormat :: Json => {
219333 // Convert to a more friendly JSON structure
@@ -264,35 +378,87 @@ pub fn codeowners_list_tags(
264378 // Process the tags from the cache
265379 match format {
266380 OutputFormat :: Text => {
267- println ! ( "CODEOWNERS Tags Report" ) ;
268- println ! ( "======================\n " ) ;
381+ // Column widths for the table
382+ let tag_width = 30 ; // For tag name
383+ let count_width = 10 ; // For file count
384+ let files_width = 60 ; // For sample files
385+
386+ println ! (
387+ "==============================================================================="
388+ ) ;
389+ println ! (
390+ " {:<tag_width$} {:<count_width$} {:<files_width$}" ,
391+ "Tag" ,
392+ "Files" ,
393+ "Sample Files" ,
394+ tag_width = tag_width,
395+ count_width = count_width,
396+ files_width = files_width
397+ ) ;
398+ println ! (
399+ "==============================================================================="
400+ ) ;
269401
270402 if cache. tags_map . is_empty ( ) {
271- println ! ( "No tags found in the codebase." ) ;
403+ println ! ( " No tags found in the codebase." ) ;
272404 } else {
273405 // Sort tags by number of files they're associated with (descending)
274406 let mut tags_with_counts: Vec < _ > = cache. tags_map . iter ( ) . collect ( ) ;
275407 tags_with_counts. sort_by ( |a, b| b. 1 . len ( ) . cmp ( & a. 1 . len ( ) ) ) ;
276408
277409 for ( tag, paths) in tags_with_counts {
278- println ! ( "Tag: {}" , tag. 0 ) ;
279- println ! ( "Files tagged: {}" , paths. len( ) ) ;
280-
281- // List first 5 files (to avoid overwhelming output)
282- if !paths. is_empty ( ) {
283- println ! ( "Sample files:" ) ;
284- for path in paths. iter ( ) . take ( 5 ) {
285- println ! ( " - {}" , path. display( ) ) ;
410+ // Prepare sample file list - show filenames only, not full paths
411+ let file_samples = if paths. is_empty ( ) {
412+ "None" . to_string ( )
413+ } else {
414+ let samples: Vec < _ > = paths
415+ . iter ( )
416+ . take ( 5 ) // Show max 5 files as samples
417+ . map ( |p| {
418+ p. file_name ( )
419+ . map ( |f| f. to_string_lossy ( ) . to_string ( ) )
420+ . unwrap_or_else ( || p. to_string_lossy ( ) . to_string ( ) )
421+ } )
422+ . collect ( ) ;
423+
424+ let mut display = samples. join ( ", " ) ;
425+ if paths. len ( ) > 5 {
426+ display. push_str ( & format ! ( " (+{})" , paths. len( ) - 5 ) ) ;
286427 }
287428
288- if paths. len ( ) > 5 {
289- println ! ( " ... and {} more" , paths. len( ) - 5 ) ;
429+ // Truncate if too long for display
430+ if display. len ( ) > files_width {
431+ format ! ( "{}..." , & display[ 0 ..files_width - 3 ] )
432+ } else {
433+ display
290434 }
291- }
435+ } ;
292436
293- println ! ( ) ; // Empty line between tags
437+ // Display the tag name, truncate if needed
438+ let tag_display = if tag. 0 . len ( ) > tag_width {
439+ format ! ( "{}..." , & tag. 0 [ 0 ..tag_width - 3 ] )
440+ } else {
441+ tag. 0 . clone ( )
442+ } ;
443+
444+ println ! (
445+ " {:<tag_width$} {:<count_width$} {:<files_width$}" ,
446+ tag_display,
447+ paths. len( ) ,
448+ file_samples,
449+ tag_width = tag_width,
450+ count_width = count_width,
451+ files_width = files_width
452+ ) ;
294453 }
295454 }
455+ println ! (
456+ "==============================================================================="
457+ ) ;
458+ println ! ( " Total: {} tags" , cache. tags_map. len( ) ) ;
459+ println ! (
460+ "==============================================================================="
461+ ) ;
296462 }
297463 OutputFormat :: Json => {
298464 // Convert to a more friendly JSON structure
0 commit comments