Skip to content

Commit 400a0b3

Browse files
committed
imp(display): improve text display
1 parent be08b7f commit 400a0b3

File tree

2 files changed

+233
-69
lines changed

2 files changed

+233
-69
lines changed

core/src/cache.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,7 @@ pub fn sync_cache(
159159

160160
// verify the hash of the cache matches the current repo hash
161161
let current_hash = get_repo_hash(repo)?;
162-
dbg!(current_hash);
163162
let cache_hash = cache.hash;
164-
dbg!(cache_hash);
165163

166164
if cache_hash != current_hash {
167165
// parse the codeowners files and build the cache

core/src/commands.rs

Lines changed: 233 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use std::io::{self, Write};
22

33
use crate::cache::{build_cache, load_cache, store_cache, sync_cache};
44
use 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

87
use utils::app_config::AppConfig;
98
use 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

Comments
 (0)