Skip to content

Commit 610bc6a

Browse files
committed
wip: refactor list_owners and list_tags commands
1 parent 2fd8759 commit 610bc6a

File tree

2 files changed

+99
-132
lines changed

2 files changed

+99
-132
lines changed

src/core/commands/list_owners.rs

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
use crate::{
2-
core::{cache::sync_cache, types::OutputFormat},
2+
core::{cache::sync_cache, display::truncate_string, types::OutputFormat},
33
utils::error::{Error, Result},
44
};
55
use std::io::{self, Write};
6+
use tabled::{Table, Tabled};
7+
8+
#[derive(Tabled)]
9+
struct OwnerDisplay {
10+
#[tabled(rename = "Owner")]
11+
identifier: String,
12+
#[tabled(rename = "Type")]
13+
owner_type: String,
14+
#[tabled(rename = "Files")]
15+
file_count: usize,
16+
#[tabled(rename = "Sample Files")]
17+
sample_files: String,
18+
}
619

720
/// Display aggregated owner statistics and associations
821
pub(crate) fn run(
@@ -14,41 +27,17 @@ pub(crate) fn run(
1427
// Load the cache
1528
let cache = sync_cache(repo, cache_file)?;
1629

30+
// Sort owners by number of files they own (descending)
31+
let mut owners_with_counts: Vec<_> = cache.owners_map.iter().collect();
32+
owners_with_counts.sort_by(|a, b| b.1.len().cmp(&a.1.len()));
33+
1734
// Process the owners from the cache
1835
match format {
1936
OutputFormat::Text => {
20-
// Column widths for the table
21-
let owner_width = 35; // For owner identifiers
22-
let type_width = 10; // For owner type
23-
let count_width = 10; // For file count
24-
let file_width = 45; // For sample files
25-
26-
println!(
27-
"==============================================================================="
28-
);
29-
println!(
30-
" {:<owner_width$} {:<type_width$} {:<count_width$} {:<file_width$}",
31-
"Owner",
32-
"Type",
33-
"Files",
34-
"Sample Files",
35-
owner_width = owner_width,
36-
type_width = type_width,
37-
count_width = count_width,
38-
file_width = file_width
39-
);
40-
println!(
41-
"==============================================================================="
42-
);
43-
44-
if cache.owners_map.is_empty() {
45-
println!(" No owners found in the codebase.");
46-
} else {
47-
// Sort owners by number of files they own (descending)
48-
let mut owners_with_counts: Vec<_> = cache.owners_map.iter().collect();
49-
owners_with_counts.sort_by(|a, b| b.1.len().cmp(&a.1.len()));
50-
51-
for (owner, paths) in owners_with_counts {
37+
// Create table data
38+
let table_data: Vec<OwnerDisplay> = owners_with_counts
39+
.iter()
40+
.map(|(owner, paths)| {
5241
// Prepare sample file list
5342
let file_samples = if paths.is_empty() {
5443
"None".to_string()
@@ -71,37 +60,37 @@ pub(crate) fn run(
7160
display
7261
};
7362

74-
// Trim the owner identifier if too long
75-
let owner_display = if owner.identifier.len() > owner_width {
76-
format!("{}...", &owner.identifier[0..owner_width - 3])
77-
} else {
78-
owner.identifier.clone()
79-
};
63+
OwnerDisplay {
64+
identifier: truncate_string(&owner.identifier, 35),
65+
owner_type: format!("{:?}", owner.owner_type),
66+
file_count: paths.len(),
67+
sample_files: truncate_string(&file_samples, 45),
68+
}
69+
})
70+
.collect();
71+
72+
// Get terminal width, fallback to 80 if unavailable
73+
let terminal_width =
74+
if let Some((terminal_size::Width(w), _)) = terminal_size::terminal_size() {
75+
w as usize
76+
} else {
77+
80
78+
};
79+
80+
let mut table = Table::new(table_data);
81+
table
82+
.with(tabled::settings::Style::modern())
83+
.with(tabled::settings::Width::wrap(
84+
terminal_width.saturating_sub(4),
85+
))
86+
.with(tabled::settings::Padding::new(1, 1, 0, 0));
8087

81-
println!(
82-
" {:<owner_width$} {:<type_width$} {:<count_width$} {:<file_width$}",
83-
owner_display,
84-
owner.owner_type,
85-
paths.len(),
86-
file_samples,
87-
owner_width = owner_width,
88-
type_width = type_width,
89-
count_width = count_width,
90-
file_width = file_width
91-
);
92-
}
93-
}
94-
println!(
95-
"==============================================================================="
96-
);
97-
println!(" Total: {} owners", cache.owners_map.len());
98-
println!(
99-
"==============================================================================="
100-
);
88+
println!("{}", table);
89+
println!("Total: {} owners", cache.owners_map.len());
10190
}
10291
OutputFormat::Json => {
10392
// Convert to a more friendly JSON structure
104-
let owners_data: Vec<_> = cache.owners_map.iter()
93+
let owners_data: Vec<_> = owners_with_counts.iter()
10594
.map(|(owner, paths)| {
10695
serde_json::json!({
10796
"identifier": owner.identifier,
@@ -116,7 +105,7 @@ pub(crate) fn run(
116105
}
117106
OutputFormat::Bincode => {
118107
let encoded =
119-
bincode::serde::encode_to_vec(&cache.owners_map, bincode::config::standard())
108+
bincode::serde::encode_to_vec(&owners_with_counts, bincode::config::standard())
120109
.map_err(|e| Error::new(&format!("Serialization error: {}", e)))?;
121110

122111
// Write raw binary bytes to stdout
@@ -126,9 +115,5 @@ pub(crate) fn run(
126115
}
127116
}
128117

129-
println!(
130-
"Owners listing completed - {} owners found",
131-
cache.owners_map.len()
132-
);
133118
Ok(())
134119
}

src/core/commands/list_tags.rs

Lines changed: 49 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
use crate::{
2-
core::{cache::sync_cache, types::OutputFormat},
2+
core::{cache::sync_cache, display::truncate_string, types::OutputFormat},
33
utils::error::{Error, Result},
44
};
55
use std::io::{self, Write};
6+
use tabled::{Table, Tabled};
7+
8+
#[derive(Tabled)]
9+
struct TagDisplay {
10+
#[tabled(rename = "Tag")]
11+
name: String,
12+
#[tabled(rename = "Files")]
13+
file_count: usize,
14+
#[tabled(rename = "Sample Files")]
15+
sample_files: String,
16+
}
617

718
/// Audit and analyze tag usage across CODEOWNERS files
819
pub(crate) fn run(
@@ -14,38 +25,17 @@ pub(crate) fn run(
1425
// Load the cache
1526
let cache = sync_cache(repo, cache_file)?;
1627

28+
// Sort tags by number of files they're associated with (descending)
29+
let mut tags_with_counts: Vec<_> = cache.tags_map.iter().collect();
30+
tags_with_counts.sort_by(|a, b| b.1.len().cmp(&a.1.len()));
31+
1732
// Process the tags from the cache
1833
match format {
1934
OutputFormat::Text => {
20-
// Column widths for the table
21-
let tag_width = 30; // For tag name
22-
let count_width = 10; // For file count
23-
let files_width = 60; // For sample files
24-
25-
println!(
26-
"==============================================================================="
27-
);
28-
println!(
29-
" {:<tag_width$} {:<count_width$} {:<files_width$}",
30-
"Tag",
31-
"Files",
32-
"Sample Files",
33-
tag_width = tag_width,
34-
count_width = count_width,
35-
files_width = files_width
36-
);
37-
println!(
38-
"==============================================================================="
39-
);
40-
41-
if cache.tags_map.is_empty() {
42-
println!(" No tags found in the codebase.");
43-
} else {
44-
// Sort tags by number of files they're associated with (descending)
45-
let mut tags_with_counts: Vec<_> = cache.tags_map.iter().collect();
46-
tags_with_counts.sort_by(|a, b| b.1.len().cmp(&a.1.len()));
47-
48-
for (tag, paths) in tags_with_counts {
35+
// Create table data
36+
let table_data: Vec<TagDisplay> = tags_with_counts
37+
.iter()
38+
.map(|(tag, paths)| {
4939
// Prepare sample file list - show filenames only, not full paths
5040
let file_samples = if paths.is_empty() {
5141
"None".to_string()
@@ -64,44 +54,39 @@ pub(crate) fn run(
6454
if paths.len() > 5 {
6555
display.push_str(&format!(" (+{})", paths.len() - 5));
6656
}
67-
68-
// Truncate if too long for display
69-
if display.len() > files_width {
70-
format!("{}...", &display[0..files_width - 3])
71-
} else {
72-
display
73-
}
57+
display
7458
};
7559

76-
// Display the tag name, truncate if needed
77-
let tag_display = if tag.0.len() > tag_width {
78-
format!("{}...", &tag.0[0..tag_width - 3])
79-
} else {
80-
tag.0.clone()
81-
};
60+
TagDisplay {
61+
name: truncate_string(&tag.0, 30),
62+
file_count: paths.len(),
63+
sample_files: truncate_string(&file_samples, 60),
64+
}
65+
})
66+
.collect();
67+
68+
// Get terminal width, fallback to 80 if unavailable
69+
let terminal_width =
70+
if let Some((terminal_size::Width(w), _)) = terminal_size::terminal_size() {
71+
w as usize
72+
} else {
73+
80
74+
};
8275

83-
println!(
84-
" {:<tag_width$} {:<count_width$} {:<files_width$}",
85-
tag_display,
86-
paths.len(),
87-
file_samples,
88-
tag_width = tag_width,
89-
count_width = count_width,
90-
files_width = files_width
91-
);
92-
}
93-
}
94-
println!(
95-
"==============================================================================="
96-
);
97-
println!(" Total: {} tags", cache.tags_map.len());
98-
println!(
99-
"==============================================================================="
100-
);
76+
let mut table = Table::new(table_data);
77+
table
78+
.with(tabled::settings::Style::modern())
79+
.with(tabled::settings::Width::wrap(
80+
terminal_width.saturating_sub(4),
81+
))
82+
.with(tabled::settings::Padding::new(1, 1, 0, 0));
83+
84+
println!("{}", table);
85+
println!("Total: {} tags", cache.tags_map.len());
10186
}
10287
OutputFormat::Json => {
10388
// Convert to a more friendly JSON structure
104-
let tags_data: Vec<_> = cache.tags_map.iter()
89+
let tags_data: Vec<_> = tags_with_counts.iter()
10590
.map(|(tag, paths)| {
10691
serde_json::json!({
10792
"name": tag.0,
@@ -115,7 +100,7 @@ pub(crate) fn run(
115100
}
116101
OutputFormat::Bincode => {
117102
let encoded =
118-
bincode::serde::encode_to_vec(&cache.tags_map, bincode::config::standard())
103+
bincode::serde::encode_to_vec(&tags_with_counts, bincode::config::standard())
119104
.map_err(|e| Error::new(&format!("Serialization error: {}", e)))?;
120105

121106
// Write raw binary bytes to stdout
@@ -125,9 +110,6 @@ pub(crate) fn run(
125110
}
126111
}
127112

128-
println!(
129-
"Tags listing completed - {} tags found",
130-
cache.tags_map.len()
131-
);
132113
Ok(())
133114
}
115+

0 commit comments

Comments
 (0)