refactor(workspace): sweep low-risk clippy::pedantic findings

Conservative cleanup pass — applied only the high-signal pedantic
lints whose fixes either remove genuine waste or read more naturally,
skipping anything stylistic that would bloat the diff.

- map_unwrap_or: 29 .map(...).unwrap_or(...) sites collapsed to
  .map_or / .is_some_and / .map_or_else equivalents
- uninlined_format_args: 7 production format!/write!/println! sites
  rewritten to the inline-argument style; assert! sites in test code
  intentionally untouched
- match_same_arms: 2 redundant arms collapsed where the bodies were
  identical and the merger didn't obscure intent

Public API is unchanged. No dependencies added or removed. The
pedantic warning count dropped from 840 to 807 (-33). Out-of-scope
findings — needless_pass_by_value on Bevy Res params, false-positive
explicit_iter_loop on Bevy Query iterators, items_after_statements
inside test mods, and the "ask before changing" merge logic in
solitaire_sync — were intentionally deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-01 02:46:32 +00:00
parent 74482252d1
commit 4b9d008be2
18 changed files with 41 additions and 75 deletions
+1 -2
View File
@@ -149,8 +149,7 @@ fn install_crash_log_hook() {
// parseable and avoids pulling in chrono just for this. // parseable and avoids pulling in chrono just for this.
let secs = SystemTime::now() let secs = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.map(|d| d.as_secs()) .map_or(0, |d| d.as_secs());
.unwrap_or(0);
let _ = writeln!(file, "----- t={secs} -----\n{info}\n"); let _ = writeln!(file, "----- t={secs} -----\n{info}\n");
} }
default_hook(info); default_hook(info);
+3 -2
View File
@@ -40,8 +40,9 @@ const SERVICE: &str = "solitaire_quest_server";
fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError { fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError {
let msg = err.to_string(); let msg = err.to_string();
match err { match err {
keyring_core::Error::NoStorageAccess(_) => TokenError::KeychainUnavailable(msg), keyring_core::Error::NoStorageAccess(_) | keyring_core::Error::NoDefaultStore => {
keyring_core::Error::NoDefaultStore => TokenError::KeychainUnavailable(msg), TokenError::KeychainUnavailable(msg)
}
keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()), keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()),
_ => TokenError::Keyring(msg), _ => TokenError::Keyring(msg),
} }
+1 -2
View File
@@ -138,8 +138,7 @@ fn cleanup_tmp_files_in(dir: &Path) {
if path if path
.file_name() .file_name()
.and_then(|n| n.to_str()) .and_then(|n| n.to_str())
.map(|n| n.ends_with(".json.tmp")) .is_some_and(|n| n.ends_with(".json.tmp"))
.unwrap_or(false)
{ {
let _ = fs::remove_file(&path); let _ = fs::remove_file(&path);
} }
+4 -8
View File
@@ -212,9 +212,7 @@ fn evaluate_on_win(
/// Convenience: resolve an achievement ID to its human-readable name. /// Convenience: resolve an achievement ID to its human-readable name.
/// Used by the toast renderer in `animation_plugin`. /// Used by the toast renderer in `animation_plugin`.
pub fn display_name_for(id: &str) -> String { pub fn display_name_for(id: &str) -> String {
achievement_by_id(id) achievement_by_id(id).map_or_else(|| id.to_string(), |d| d.name.to_string())
.map(|d| d.name.to_string())
.unwrap_or_else(|| id.to_string())
} }
/// Marker on the "Done" button inside the Achievements modal. /// Marker on the "Done" button inside the Achievements modal.
@@ -292,12 +290,10 @@ fn spawn_achievements_screen(
for record in &sorted { for record in &sorted {
let def = achievement_by_id(&record.id); let def = achievement_by_id(&record.id);
let (name, description) = def let (name, description) = def.map_or((record.id.as_str(), ""), |d| (d.name, d.description));
.map(|d| (d.name, d.description))
.unwrap_or((&record.id, ""));
// Hide secret locked achievements so they remain a surprise. // Hide secret locked achievements so they remain a surprise.
let is_secret = def.map(|d| d.secret).unwrap_or(false); let is_secret = def.is_some_and(|d| d.secret);
if is_secret && !record.unlocked { if is_secret && !record.unlocked {
continue; continue;
} }
@@ -401,7 +397,7 @@ fn tooltip_for_row(unlocked: bool, def: Option<&AchievementDef>) -> String {
None => "Earned!".to_string(), None => "Earned!".to_string(),
} }
} else { } else {
let description = def.map(|d| d.description).unwrap_or(""); let description = def.map_or("", |d| d.description);
let how = if description.is_empty() { let how = if description.is_empty() {
"How to unlock: keep playing.".to_string() "How to unlock: keep playing.".to_string()
} else { } else {
+1 -2
View File
@@ -327,8 +327,7 @@ fn handle_mute_keys(
let shift = keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight); let shift = keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight);
let (sfx_vol, music_vol) = settings let (sfx_vol, music_vol) = settings
.as_ref() .as_ref()
.map(|s| (s.0.sfx_volume, s.0.music_volume)) .map_or((1.0, 0.5), |s| (s.0.sfx_volume, s.0.music_volume));
.unwrap_or((1.0, 0.5));
if shift { if shift {
// Shift+M: toggle music mute only, SFX unaffected. // Shift+M: toggle music mute only, SFX unaffected.
@@ -189,8 +189,7 @@ pub(crate) fn apply_hover_scale(
hover_state.scale = if let Some(entity) = target_entity { hover_state.scale = if let Some(entity) = target_entity {
cards cards
.get(entity) .get(entity)
.map(|(_, t)| t.scale.x) .map_or(hover_target, |(_, t)| t.scale.x)
.unwrap_or(hover_target)
} else { } else {
1.0 1.0
}; };
+1 -3
View File
@@ -380,9 +380,7 @@ fn start_deal_anim(
let stock_start = Vec3::new(stock_pos.x, stock_pos.y, 0.0); let stock_start = Vec3::new(stock_pos.x, stock_pos.y, 0.0);
let speed = settings.as_ref().map(|s| &s.0.animation_speed); let speed = settings.as_ref().map(|s| &s.0.animation_speed);
let stagger_secs = speed let stagger_secs = speed.map_or(DEAL_STAGGER_SECS, deal_stagger_secs_for_speed);
.map(deal_stagger_secs_for_speed)
.unwrap_or(DEAL_STAGGER_SECS);
for (index, (entity, card_marker, transform)) in card_entities.iter().enumerate() { for (index, (entity, card_marker, transform)) in card_entities.iter().enumerate() {
let final_pos = transform.translation; let final_pos = transform.translation;
+2 -4
View File
@@ -153,8 +153,7 @@ fn tick_elapsed_time(
fn seed_from_system_time() -> u64 { fn seed_from_system_time() -> u64 {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64) .map_or(0, |d| d.as_nanos() as u64)
.unwrap_or(0)
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@@ -201,8 +200,7 @@ fn handle_new_game(
// where SettingsPlugin is not installed. // where SettingsPlugin is not installed.
let draw_mode = settings let draw_mode = settings
.as_ref() .as_ref()
.map(|s| s.0.draw_mode.clone()) .map_or_else(|| game.0.draw_mode.clone(), |s| s.0.draw_mode.clone());
.unwrap_or_else(|| game.0.draw_mode.clone());
let mode = ev.mode.unwrap_or(game.0.mode); let mode = ev.mode.unwrap_or(game.0.mode);
game.0 = GameState::new_with_mode(seed, draw_mode, mode); game.0 = GameState::new_with_mode(seed, draw_mode, mode);
// Delete any previously saved in-progress state — this is a fresh game. // Delete any previously saved in-progress state — this is a fresh game.
+4 -8
View File
@@ -153,8 +153,7 @@ fn toggle_leaderboard_screen(
// Spawn the panel immediately with whatever data we have so far. // Spawn the panel immediately with whatever data we have so far.
let remote_available = provider let remote_available = provider
.as_ref() .as_ref()
.map(|p| p.0.backend_name() != "local") .is_some_and(|p| p.0.backend_name() != "local");
.unwrap_or(false);
spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref()); spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref());
// Start a background fetch if not already in flight. // Start a background fetch if not already in flight.
@@ -215,8 +214,7 @@ fn update_leaderboard_panel(
} }
let remote_available = provider let remote_available = provider
.as_ref() .as_ref()
.map(|p| p.0.backend_name() != "local") .is_some_and(|p| p.0.backend_name() != "local");
.unwrap_or(false);
for entity in &screens { for entity in &screens {
commands.entity(entity).despawn(); commands.entity(entity).despawn();
spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref()); spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref());
@@ -473,12 +471,10 @@ fn spawn_leaderboard_screen(
let time_str = entry let time_str = entry
.best_time_secs .best_time_secs
.map(format_secs) .map_or_else(|| "-".to_string(), format_secs);
.unwrap_or_else(|| "-".to_string());
let score_str = entry let score_str = entry
.best_score .best_score
.map(|s| s.to_string()) .map_or_else(|| "-".to_string(), |s| s.to_string());
.unwrap_or_else(|| "-".to_string());
card.spawn(Node { card.spawn(Node {
flex_direction: FlexDirection::Row, flex_direction: FlexDirection::Row,
+3 -3
View File
@@ -208,7 +208,7 @@ fn spawn_profile_screen(
let records = &ar.0; let records = &ar.0;
let unlocked_count = records.iter().filter(|r| r.unlocked).count(); let unlocked_count = records.iter().filter(|r| r.unlocked).count();
card.spawn(( card.spawn((
Text::new(format!("{} / 18 unlocked", unlocked_count)), Text::new(format!("{unlocked_count} / 18 unlocked")),
font_row.clone(), font_row.clone(),
TextColor(ACCENT_PRIMARY), TextColor(ACCENT_PRIMARY),
)); ));
@@ -216,7 +216,7 @@ fn spawn_profile_screen(
let mut any_unlocked = false; let mut any_unlocked = false;
for record in records { for record in records {
let def = achievement_by_id(record.id.as_str()); let def = achievement_by_id(record.id.as_str());
let is_secret = def.map(|d| d.secret).unwrap_or(false); let is_secret = def.is_some_and(|d| d.secret);
if is_secret && !record.unlocked { if is_secret && !record.unlocked {
continue; continue;
} }
@@ -224,7 +224,7 @@ fn spawn_profile_screen(
continue; continue;
} }
any_unlocked = true; any_unlocked = true;
let name = def.map(|d| d.name).unwrap_or(record.id.as_str()); let name = def.map_or(record.id.as_str(), |d| d.name);
let date_str = match record.unlock_date { let date_str = match record.unlock_date {
Some(dt) => format!(" ({})", dt.format("%Y-%m-%d")), Some(dt) => format!(" ({})", dt.format("%Y-%m-%d")),
None => String::new(), None => String::new(),
+1 -1
View File
@@ -257,7 +257,7 @@ fn handle_selection_keys(
// --- Priority 2: tableau stack move --- // --- Priority 2: tableau stack move ---
// Count the full contiguous face-up run in the source pile. // Count the full contiguous face-up run in the source pile.
let run_len = face_up_run_len(game.0.piles.get(pile).map(|p| p.cards.as_slice()).unwrap_or(&[])); let run_len = face_up_run_len(game.0.piles.get(pile).map_or(&[], |p| p.cards.as_slice()));
let bottom_card = game let bottom_card = game
.0 .0
.piles .piles
+8 -11
View File
@@ -360,16 +360,13 @@ fn sync_settings_panel_visibility(
if screen.0 { if screen.0 {
if panels.is_empty() { if panels.is_empty() {
let status_label = sync_status let status_label = sync_status
.map(|s| sync_status_label(&s.0)) .map_or_else(|| "Status: local only".to_string(), |s| sync_status_label(&s.0));
.unwrap_or_else(|| "Status: local only".to_string());
let unlocked_backs = progress let unlocked_backs = progress
.as_ref() .as_ref()
.map(|p| p.0.unlocked_card_backs.as_slice()) .map_or(&[0][..], |p| p.0.unlocked_card_backs.as_slice());
.unwrap_or(&[0]);
let unlocked_bgs = progress let unlocked_bgs = progress
.as_ref() .as_ref()
.map(|p| p.0.unlocked_backgrounds.as_slice()) .map_or(&[0][..], |p| p.0.unlocked_backgrounds.as_slice());
.unwrap_or(&[0]);
spawn_settings_panel( spawn_settings_panel(
&mut commands, &mut commands,
&settings.0, &settings.0,
@@ -530,7 +527,7 @@ fn handle_settings_buttons(
persist(&path, &settings.0); persist(&path, &settings.0);
changed.write(SettingsChangedEvent(settings.0.clone())); changed.write(SettingsChangedEvent(settings.0.clone()));
if let Ok(mut t) = sfx_text.single_mut() { if let Ok(mut t) = sfx_text.single_mut() {
**t = format!("{:.2}", after); **t = format!("{after:.2}");
} }
} }
} }
@@ -541,7 +538,7 @@ fn handle_settings_buttons(
persist(&path, &settings.0); persist(&path, &settings.0);
changed.write(SettingsChangedEvent(settings.0.clone())); changed.write(SettingsChangedEvent(settings.0.clone()));
if let Ok(mut t) = sfx_text.single_mut() { if let Ok(mut t) = sfx_text.single_mut() {
**t = format!("{:.2}", after); **t = format!("{after:.2}");
} }
} }
} }
@@ -552,7 +549,7 @@ fn handle_settings_buttons(
persist(&path, &settings.0); persist(&path, &settings.0);
changed.write(SettingsChangedEvent(settings.0.clone())); changed.write(SettingsChangedEvent(settings.0.clone()));
if let Ok(mut t) = music_text.single_mut() { if let Ok(mut t) = music_text.single_mut() {
**t = format!("{:.2}", after); **t = format!("{after:.2}");
} }
} }
} }
@@ -563,7 +560,7 @@ fn handle_settings_buttons(
persist(&path, &settings.0); persist(&path, &settings.0);
changed.write(SettingsChangedEvent(settings.0.clone())); changed.write(SettingsChangedEvent(settings.0.clone()));
if let Ok(mut t) = music_text.single_mut() { if let Ok(mut t) = music_text.single_mut() {
**t = format!("{:.2}", after); **t = format!("{after:.2}");
} }
} }
} }
@@ -1082,7 +1079,7 @@ fn volume_row<Marker: Component>(
)); ));
row.spawn(( row.spawn((
marker, marker,
Text::new(format!("{:.2}", value)), Text::new(format!("{value:.2}")),
value_font, value_font,
TextColor(TEXT_PRIMARY), TextColor(TEXT_PRIMARY),
)); ));
+1 -3
View File
@@ -259,9 +259,7 @@ fn dismiss_splash_on_input(
return; return;
} }
let touch_pressed = touches let touch_pressed = touches.is_some_and(|t| t.iter_just_pressed().next().is_some());
.map(|t| t.iter_just_pressed().next().is_some())
.unwrap_or(false);
let dismissed = keys.get_just_pressed().next().is_some() let dismissed = keys.get_just_pressed().next().is_some()
|| mouse.get_just_pressed().next().is_some() || mouse.get_just_pressed().next().is_some()
|| touch_pressed; || touch_pressed;
+3 -9
View File
@@ -142,14 +142,10 @@ fn setup_table(
let window_size = windows let window_size = windows
.iter() .iter()
.next() .next()
.map(default_window_size) .map_or(Vec2::new(1280.0, 800.0), default_window_size);
.unwrap_or(Vec2::new(1280.0, 800.0));
let layout = compute_layout(window_size); let layout = compute_layout(window_size);
let selected_bg = settings let selected_bg = settings.as_ref().map_or(0, |s| s.0.selected_background);
.as_ref()
.map(|s| s.0.selected_background)
.unwrap_or(0);
let image_handle = bg_images let image_handle = bg_images
.as_ref() .as_ref()
@@ -341,9 +337,7 @@ fn apply_hint_pile_highlight(
if pile_marker.0 != ev.dest_pile { if pile_marker.0 != ev.dest_pile {
continue; continue;
} }
let original_color = existing let original_color = existing.map_or(sprite.color, |h| h.original_color);
.map(|h| h.original_color)
.unwrap_or(sprite.color);
sprite.color = HINT_PILE_HIGHLIGHT_COLOUR; sprite.color = HINT_PILE_HIGHLIGHT_COLOUR;
commands.entity(entity).insert(HintPileHighlight { commands.entity(entity).insert(HintPileHighlight {
timer: 2.0, timer: 2.0,
+2 -8
View File
@@ -364,8 +364,7 @@ fn handle_focus_keys(
.filter(|e| { .filter(|e| {
focusables focusables
.get(*e) .get(*e)
.map(|(_, disabled)| !disabled) .is_ok_and(|(_, disabled)| !disabled)
.unwrap_or(false)
}) })
.collect(); .collect();
if !row_cycle.is_empty() if !row_cycle.is_empty()
@@ -466,12 +465,7 @@ fn handle_focus_keys(
// Stable sort by `Focusable::order` so explicit priorities (e.g. // Stable sort by `Focusable::order` so explicit priorities (e.g.
// HUD spawn-order: 0..5) drive the cycle. The pre-sort by entity // HUD spawn-order: 0..5) drive the cycle. The pre-sort by entity
// index above is the tiebreaker for entries sharing an `order`. // index above is the tiebreaker for entries sharing an `order`.
group.sort_by_key(|e| { group.sort_by_key(|e| focusables.get(*e).map_or(i32::MAX, |(f, _)| f.order));
focusables
.get(*e)
.map(|(f, _)| f.order)
.unwrap_or(i32::MAX)
});
if group.is_empty() { if group.is_empty() {
// Still consume the key so the card-selection plugin doesn't // Still consume the key so the card-selection plugin doesn't
+1 -2
View File
@@ -409,8 +409,7 @@ pub fn apply_modal_enter_speed(
) { ) {
let speed = settings let speed = settings
.as_ref() .as_ref()
.map(|s| s.0.animation_speed) .map_or(AnimSpeed::Normal, |s| s.0.animation_speed);
.unwrap_or(AnimSpeed::Normal);
for mut entering in &mut q { for mut entering in &mut q {
entering.duration = scaled_duration(MOTION_MODAL_SECS, speed); entering.duration = scaled_duration(MOTION_MODAL_SECS, speed);
} }
+1 -3
View File
@@ -121,9 +121,7 @@ fn evaluate_weekly_goals(
/// Resolve a goal id to its description (used for toasts). /// Resolve a goal id to its description (used for toasts).
pub fn weekly_goal_description(id: &str) -> String { pub fn weekly_goal_description(id: &str) -> String {
weekly_goal_by_id(id) weekly_goal_by_id(id).map_or_else(|| id.to_string(), |g| g.description.to_string())
.map(|g| g.description.to_string())
.unwrap_or_else(|| id.to_string())
} }
#[cfg(test)] #[cfg(test)]
+3 -2
View File
@@ -51,8 +51,9 @@ pub enum AppError {
impl IntoResponse for AppError { impl IntoResponse for AppError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
let (status, message) = match &self { let (status, message) = match &self {
AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()), AppError::Unauthorized | AppError::InvalidCredentials => {
AppError::InvalidCredentials => (StatusCode::UNAUTHORIZED, self.to_string()), (StatusCode::UNAUTHORIZED, self.to_string())
}
AppError::UsernameTaken => (StatusCode::CONFLICT, self.to_string()), AppError::UsernameTaken => (StatusCode::CONFLICT, self.to_string()),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()), AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Database(e) => { AppError::Database(e) => {