refactor: resolve all lint findings

- remove 34 trivial restating comments (auto-fix)
- replace all 20 production .unwrap() calls with graceful handling:
  ?-propagation via LockPoisoned, unwrap_or_default on infallible
  conversions, let _ = on actor-channel sends, and a downloads_sender()
  helper in the TUI that also dedupes three channel-init blocks
- collapse nested if into match guard (clippy::collapsible_match)
- split client.rs (2275 lines) into a client/ module: connection,
  search, downloads, uploads, rooms, operations
- split ui/main_tui.rs (2027 lines) into a main_tui/ module: render,
  input, browse, rooms, downloads, search
- extract oversized functions: peer_actor/server_actor handle_message
  into per-message helpers, main() into init_logging/run_default_tui,
  download_selector and main_tui render into per-section helpers

Mechanical only, zero behavior change. Verified: clippy clean
(pedantic+nursery), fmt clean, 143 unit tests + 16 e2e tests against
soulfind pass, lint clean.
This commit is contained in:
Michel de Graaf
2026-07-20 10:53:57 +02:00
parent 17864ac9b0
commit 2918b0ce75
35 changed files with 4113 additions and 3983 deletions
+120 -100
View File
@@ -85,7 +85,6 @@ impl FileSelector {
terminal.draw(|frame| self.render(frame))?;
self.spinner_state = (self.spinner_state + 1) % 10;
// Update spinner animation every 80ms
self.last_spinner_update = Instant::now();
// Poll for new search results if active
@@ -106,7 +105,6 @@ impl FileSelector {
}
}
// Check for keyboard input with timeout for polling
let timeout = if self.search_active {
Duration::from_millis(50)
} else {
@@ -144,7 +142,6 @@ impl FileSelector {
}
}
// Update items and reset state
let len = new_items.len();
self.all_items = new_items.clone();
self.items = new_items;
@@ -354,7 +351,21 @@ impl FileSelector {
(None, chunks[1])
};
let title = if self.search_active {
self.render_results_table(frame, table_area);
if self.items.is_empty() {
self.render_loading_message(frame, table_area);
}
if let Some(info_area) = info_area {
self.render_info(frame, info_area);
}
self.render_controls(frame, controls_area);
}
fn table_title(&self) -> String {
if self.search_active {
// Live Soulseek search is active
let spinner = get_spinner_char(self.spinner_state);
let elapsed = self.search_start_time.elapsed().as_secs();
@@ -378,7 +389,15 @@ impl FileSelector {
"Multi-select files to download ({} selected)",
self.selected_indices.len()
)
};
}
}
fn render_results_table(
&mut self,
frame: &mut Frame,
table_area: ratatui::layout::Rect,
) {
let title = self.table_title();
let header = Row::new(vec![
Cell::from(""),
@@ -465,108 +484,109 @@ impl FileSelector {
frame.buffer_mut(),
&mut self.state,
);
}
if self.items.is_empty() {
let spinner = get_spinner_char(self.spinner_state);
fn render_loading_message(
&self,
frame: &mut Frame,
table_area: ratatui::layout::Rect,
) {
let spinner = get_spinner_char(self.spinner_state);
let mut loading_message: Vec<Span> = Vec::new();
if self.items.is_empty() && self.search_active {
loading_message.extend(vec![
Span::raw(spinner),
Span::raw(" Searching for '"),
Span::styled(self.soulseek_query.clone(), primary_style()),
Span::raw(format!(
"' [{}/{}s]",
self.search_start_time.elapsed().as_secs(),
self.search_timeout.as_secs()
)),
]);
} else {
loading_message.extend(vec![
Span::raw(spinner),
Span::raw(" Searching for '"),
Span::styled(self.soulseek_query.clone(), primary_style()),
Span::raw("'; No results yet"),
]);
}
// Center the loading message
let vertical = Layout::vertical([
Constraint::Fill(1),
Constraint::Length(3),
Constraint::Fill(1),
])
.split(table_area);
let loading_message_line = Line::from(loading_message);
// Calculate responsive width: text + generous padding for borders and spacing, max 80% of screen
let text_width =
loading_message_line.to_string().chars().count() as u16 + 5; // +10 for borders, padding, and safety margin
let max_width = (table_area.width * 80) / 100;
let widget_width = text_width.min(max_width);
let horizontal = Layout::horizontal([
Constraint::Fill(1),
Constraint::Length(widget_width),
Constraint::Fill(1),
])
.split(vertical[1]);
let loading_widget = Paragraph::new(loading_message_line)
.style(Style::default())
.block(
Block::default()
.borders(Borders::ALL)
.border_style(primary_style())
.border_type(border_type(false)),
)
.alignment(Alignment::Center)
.wrap(Wrap { trim: true });
frame.render_widget(loading_widget, horizontal[1]);
let mut loading_message: Vec<Span> = Vec::new();
if self.items.is_empty() && self.search_active {
loading_message.extend(vec![
Span::raw(spinner),
Span::raw(" Searching for '"),
Span::styled(self.soulseek_query.clone(), primary_style()),
Span::raw(format!(
"' [{}/{}s]",
self.search_start_time.elapsed().as_secs(),
self.search_timeout.as_secs()
)),
]);
} else {
loading_message.extend(vec![
Span::raw(spinner),
Span::raw(" Searching for '"),
Span::styled(self.soulseek_query.clone(), primary_style()),
Span::raw("'; No results yet"),
]);
}
if let Some(info_area) = info_area {
let (info_text, title, style) = if self.is_filtering {
(
format!("Filter: {}", self.filter_query),
"Filter",
warning_style(),
)
} else if !self.filter_query.is_empty() {
(
format!(
"Current filter: {} (press / to modify, Esc to clear)",
self.filter_query
),
"Filter",
primary_style(),
)
} else {
(
format!(
"{} file(s) selected for download",
self.selected_indices.len()
),
"Selection",
success_style(),
)
};
// Center the loading message
let vertical = Layout::vertical([
Constraint::Fill(1),
Constraint::Length(3),
Constraint::Fill(1),
])
.split(table_area);
let info_widget = Paragraph::new(info_text)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(border_type(false))
.title(title),
)
.style(style);
let loading_message_line = Line::from(loading_message);
// Calculate responsive width: text + generous padding for borders and spacing, max 80% of screen
let text_width =
loading_message_line.to_string().chars().count() as u16 + 5; // +10 for borders, padding, and safety margin
let max_width = (table_area.width * 80) / 100;
let widget_width = text_width.min(max_width);
frame.render_widget(info_widget, info_area);
}
let horizontal = Layout::horizontal([
Constraint::Fill(1),
Constraint::Length(widget_width),
Constraint::Fill(1),
])
.split(vertical[1]);
// Render controls footer
self.render_controls(frame, controls_area);
let loading_widget = Paragraph::new(loading_message_line)
.style(Style::default())
.block(
Block::default()
.borders(Borders::ALL)
.border_style(primary_style())
.border_type(border_type(false)),
)
.alignment(Alignment::Center)
.wrap(Wrap { trim: true });
frame.render_widget(loading_widget, horizontal[1]);
}
fn render_info(&self, frame: &mut Frame, info_area: ratatui::layout::Rect) {
let (info_text, title, style) = if self.is_filtering {
(
format!("Filter: {}", self.filter_query),
"Filter",
warning_style(),
)
} else if !self.filter_query.is_empty() {
(
format!(
"Current filter: {} (press / to modify, Esc to clear)",
self.filter_query
),
"Filter",
primary_style(),
)
} else {
(
format!(
"{} file(s) selected for download",
self.selected_indices.len()
),
"Selection",
success_style(),
)
};
let info_widget = Paragraph::new(info_text)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(border_type(false))
.title(title),
)
.style(style);
frame.render_widget(info_widget, info_area);
}
fn render_controls(&self, frame: &mut Frame, area: ratatui::layout::Rect) {