diff --git a/solitaire_app/src/main.rs b/solitaire_app/src/main.rs index 358ace6..45039f5 100644 --- a/solitaire_app/src/main.rs +++ b/solitaire_app/src/main.rs @@ -149,8 +149,7 @@ fn install_crash_log_hook() { // parseable and avoids pulling in chrono just for this. let secs = SystemTime::now() .duration_since(UNIX_EPOCH) - .map(|d| d.as_secs()) - .unwrap_or(0); + .map_or(0, |d| d.as_secs()); let _ = writeln!(file, "----- t={secs} -----\n{info}\n"); } default_hook(info); diff --git a/solitaire_data/src/auth_tokens.rs b/solitaire_data/src/auth_tokens.rs index 653f22a..af3f37c 100644 --- a/solitaire_data/src/auth_tokens.rs +++ b/solitaire_data/src/auth_tokens.rs @@ -40,8 +40,9 @@ const SERVICE: &str = "solitaire_quest_server"; fn map_keyring_err(err: keyring_core::Error, username: &str) -> TokenError { let msg = err.to_string(); match err { - keyring_core::Error::NoStorageAccess(_) => TokenError::KeychainUnavailable(msg), - keyring_core::Error::NoDefaultStore => TokenError::KeychainUnavailable(msg), + keyring_core::Error::NoStorageAccess(_) | keyring_core::Error::NoDefaultStore => { + TokenError::KeychainUnavailable(msg) + } keyring_core::Error::NoEntry => TokenError::NotFound(username.to_string()), _ => TokenError::Keyring(msg), } diff --git a/solitaire_data/src/storage.rs b/solitaire_data/src/storage.rs index ca1d041..6fd09b0 100644 --- a/solitaire_data/src/storage.rs +++ b/solitaire_data/src/storage.rs @@ -138,8 +138,7 @@ fn cleanup_tmp_files_in(dir: &Path) { if path .file_name() .and_then(|n| n.to_str()) - .map(|n| n.ends_with(".json.tmp")) - .unwrap_or(false) + .is_some_and(|n| n.ends_with(".json.tmp")) { let _ = fs::remove_file(&path); } diff --git a/solitaire_engine/src/achievement_plugin.rs b/solitaire_engine/src/achievement_plugin.rs index 8306560..a6e5f2f 100644 --- a/solitaire_engine/src/achievement_plugin.rs +++ b/solitaire_engine/src/achievement_plugin.rs @@ -212,9 +212,7 @@ fn evaluate_on_win( /// Convenience: resolve an achievement ID to its human-readable name. /// Used by the toast renderer in `animation_plugin`. pub fn display_name_for(id: &str) -> String { - achievement_by_id(id) - .map(|d| d.name.to_string()) - .unwrap_or_else(|| id.to_string()) + achievement_by_id(id).map_or_else(|| id.to_string(), |d| d.name.to_string()) } /// Marker on the "Done" button inside the Achievements modal. @@ -292,12 +290,10 @@ fn spawn_achievements_screen( for record in &sorted { let def = achievement_by_id(&record.id); - let (name, description) = def - .map(|d| (d.name, d.description)) - .unwrap_or((&record.id, "")); + let (name, description) = def.map_or((record.id.as_str(), ""), |d| (d.name, d.description)); // 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 { continue; } @@ -401,7 +397,7 @@ fn tooltip_for_row(unlocked: bool, def: Option<&AchievementDef>) -> String { None => "Earned!".to_string(), } } else { - let description = def.map(|d| d.description).unwrap_or(""); + let description = def.map_or("", |d| d.description); let how = if description.is_empty() { "How to unlock: keep playing.".to_string() } else { diff --git a/solitaire_engine/src/audio_plugin.rs b/solitaire_engine/src/audio_plugin.rs index 6fed715..2d33813 100644 --- a/solitaire_engine/src/audio_plugin.rs +++ b/solitaire_engine/src/audio_plugin.rs @@ -327,8 +327,7 @@ fn handle_mute_keys( let shift = keys.pressed(KeyCode::ShiftLeft) || keys.pressed(KeyCode::ShiftRight); let (sfx_vol, music_vol) = settings .as_ref() - .map(|s| (s.0.sfx_volume, s.0.music_volume)) - .unwrap_or((1.0, 0.5)); + .map_or((1.0, 0.5), |s| (s.0.sfx_volume, s.0.music_volume)); if shift { // Shift+M: toggle music mute only, SFX unaffected. diff --git a/solitaire_engine/src/card_animation/interaction.rs b/solitaire_engine/src/card_animation/interaction.rs index 7a05fd1..7067e4f 100644 --- a/solitaire_engine/src/card_animation/interaction.rs +++ b/solitaire_engine/src/card_animation/interaction.rs @@ -189,8 +189,7 @@ pub(crate) fn apply_hover_scale( hover_state.scale = if let Some(entity) = target_entity { cards .get(entity) - .map(|(_, t)| t.scale.x) - .unwrap_or(hover_target) + .map_or(hover_target, |(_, t)| t.scale.x) } else { 1.0 }; diff --git a/solitaire_engine/src/feedback_anim_plugin.rs b/solitaire_engine/src/feedback_anim_plugin.rs index 72206a5..9cf4576 100644 --- a/solitaire_engine/src/feedback_anim_plugin.rs +++ b/solitaire_engine/src/feedback_anim_plugin.rs @@ -380,9 +380,7 @@ fn start_deal_anim( 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 stagger_secs = speed - .map(deal_stagger_secs_for_speed) - .unwrap_or(DEAL_STAGGER_SECS); + let stagger_secs = speed.map_or(DEAL_STAGGER_SECS, deal_stagger_secs_for_speed); for (index, (entity, card_marker, transform)) in card_entities.iter().enumerate() { let final_pos = transform.translation; diff --git a/solitaire_engine/src/game_plugin.rs b/solitaire_engine/src/game_plugin.rs index 076496b..1f31ca3 100644 --- a/solitaire_engine/src/game_plugin.rs +++ b/solitaire_engine/src/game_plugin.rs @@ -153,8 +153,7 @@ fn tick_elapsed_time( fn seed_from_system_time() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) - .map(|d| d.as_nanos() as u64) - .unwrap_or(0) + .map_or(0, |d| d.as_nanos() as u64) } #[allow(clippy::too_many_arguments)] @@ -201,8 +200,7 @@ fn handle_new_game( // where SettingsPlugin is not installed. let draw_mode = settings .as_ref() - .map(|s| s.0.draw_mode.clone()) - .unwrap_or_else(|| game.0.draw_mode.clone()); + .map_or_else(|| game.0.draw_mode.clone(), |s| s.0.draw_mode.clone()); let mode = ev.mode.unwrap_or(game.0.mode); game.0 = GameState::new_with_mode(seed, draw_mode, mode); // Delete any previously saved in-progress state — this is a fresh game. diff --git a/solitaire_engine/src/leaderboard_plugin.rs b/solitaire_engine/src/leaderboard_plugin.rs index 7ccf248..9542dff 100644 --- a/solitaire_engine/src/leaderboard_plugin.rs +++ b/solitaire_engine/src/leaderboard_plugin.rs @@ -153,8 +153,7 @@ fn toggle_leaderboard_screen( // Spawn the panel immediately with whatever data we have so far. let remote_available = provider .as_ref() - .map(|p| p.0.backend_name() != "local") - .unwrap_or(false); + .is_some_and(|p| p.0.backend_name() != "local"); spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref()); // Start a background fetch if not already in flight. @@ -215,8 +214,7 @@ fn update_leaderboard_panel( } let remote_available = provider .as_ref() - .map(|p| p.0.backend_name() != "local") - .unwrap_or(false); + .is_some_and(|p| p.0.backend_name() != "local"); for entity in &screens { commands.entity(entity).despawn(); spawn_leaderboard_screen(&mut commands, &data, remote_available, font_res.as_deref()); @@ -473,12 +471,10 @@ fn spawn_leaderboard_screen( let time_str = entry .best_time_secs - .map(format_secs) - .unwrap_or_else(|| "-".to_string()); + .map_or_else(|| "-".to_string(), format_secs); let score_str = entry .best_score - .map(|s| s.to_string()) - .unwrap_or_else(|| "-".to_string()); + .map_or_else(|| "-".to_string(), |s| s.to_string()); card.spawn(Node { flex_direction: FlexDirection::Row, diff --git a/solitaire_engine/src/profile_plugin.rs b/solitaire_engine/src/profile_plugin.rs index f8fb1be..7438206 100644 --- a/solitaire_engine/src/profile_plugin.rs +++ b/solitaire_engine/src/profile_plugin.rs @@ -208,7 +208,7 @@ fn spawn_profile_screen( let records = &ar.0; let unlocked_count = records.iter().filter(|r| r.unlocked).count(); card.spawn(( - Text::new(format!("{} / 18 unlocked", unlocked_count)), + Text::new(format!("{unlocked_count} / 18 unlocked")), font_row.clone(), TextColor(ACCENT_PRIMARY), )); @@ -216,7 +216,7 @@ fn spawn_profile_screen( let mut any_unlocked = false; for record in records { 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 { continue; } @@ -224,7 +224,7 @@ fn spawn_profile_screen( continue; } 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 { Some(dt) => format!(" ({})", dt.format("%Y-%m-%d")), None => String::new(), diff --git a/solitaire_engine/src/selection_plugin.rs b/solitaire_engine/src/selection_plugin.rs index 9060b1e..71ae2c8 100644 --- a/solitaire_engine/src/selection_plugin.rs +++ b/solitaire_engine/src/selection_plugin.rs @@ -257,7 +257,7 @@ fn handle_selection_keys( // --- Priority 2: tableau stack move --- // 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 .0 .piles diff --git a/solitaire_engine/src/settings_plugin.rs b/solitaire_engine/src/settings_plugin.rs index b98f3d1..1a79bfc 100644 --- a/solitaire_engine/src/settings_plugin.rs +++ b/solitaire_engine/src/settings_plugin.rs @@ -360,16 +360,13 @@ fn sync_settings_panel_visibility( if screen.0 { if panels.is_empty() { let status_label = sync_status - .map(|s| sync_status_label(&s.0)) - .unwrap_or_else(|| "Status: local only".to_string()); + .map_or_else(|| "Status: local only".to_string(), |s| sync_status_label(&s.0)); let unlocked_backs = progress .as_ref() - .map(|p| p.0.unlocked_card_backs.as_slice()) - .unwrap_or(&[0]); + .map_or(&[0][..], |p| p.0.unlocked_card_backs.as_slice()); let unlocked_bgs = progress .as_ref() - .map(|p| p.0.unlocked_backgrounds.as_slice()) - .unwrap_or(&[0]); + .map_or(&[0][..], |p| p.0.unlocked_backgrounds.as_slice()); spawn_settings_panel( &mut commands, &settings.0, @@ -530,7 +527,7 @@ fn handle_settings_buttons( persist(&path, &settings.0); changed.write(SettingsChangedEvent(settings.0.clone())); 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); changed.write(SettingsChangedEvent(settings.0.clone())); 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); changed.write(SettingsChangedEvent(settings.0.clone())); 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); changed.write(SettingsChangedEvent(settings.0.clone())); if let Ok(mut t) = music_text.single_mut() { - **t = format!("{:.2}", after); + **t = format!("{after:.2}"); } } } @@ -1082,7 +1079,7 @@ fn volume_row( )); row.spawn(( marker, - Text::new(format!("{:.2}", value)), + Text::new(format!("{value:.2}")), value_font, TextColor(TEXT_PRIMARY), )); diff --git a/solitaire_engine/src/splash_plugin.rs b/solitaire_engine/src/splash_plugin.rs index 2fad87a..0aa079b 100644 --- a/solitaire_engine/src/splash_plugin.rs +++ b/solitaire_engine/src/splash_plugin.rs @@ -259,9 +259,7 @@ fn dismiss_splash_on_input( return; } - let touch_pressed = touches - .map(|t| t.iter_just_pressed().next().is_some()) - .unwrap_or(false); + let touch_pressed = touches.is_some_and(|t| t.iter_just_pressed().next().is_some()); let dismissed = keys.get_just_pressed().next().is_some() || mouse.get_just_pressed().next().is_some() || touch_pressed; diff --git a/solitaire_engine/src/table_plugin.rs b/solitaire_engine/src/table_plugin.rs index 181b450..27048ef 100644 --- a/solitaire_engine/src/table_plugin.rs +++ b/solitaire_engine/src/table_plugin.rs @@ -142,14 +142,10 @@ fn setup_table( let window_size = windows .iter() .next() - .map(default_window_size) - .unwrap_or(Vec2::new(1280.0, 800.0)); + .map_or(Vec2::new(1280.0, 800.0), default_window_size); let layout = compute_layout(window_size); - let selected_bg = settings - .as_ref() - .map(|s| s.0.selected_background) - .unwrap_or(0); + let selected_bg = settings.as_ref().map_or(0, |s| s.0.selected_background); let image_handle = bg_images .as_ref() @@ -341,9 +337,7 @@ fn apply_hint_pile_highlight( if pile_marker.0 != ev.dest_pile { continue; } - let original_color = existing - .map(|h| h.original_color) - .unwrap_or(sprite.color); + let original_color = existing.map_or(sprite.color, |h| h.original_color); sprite.color = HINT_PILE_HIGHLIGHT_COLOUR; commands.entity(entity).insert(HintPileHighlight { timer: 2.0, diff --git a/solitaire_engine/src/ui_focus.rs b/solitaire_engine/src/ui_focus.rs index 5c03d14..eaaa341 100644 --- a/solitaire_engine/src/ui_focus.rs +++ b/solitaire_engine/src/ui_focus.rs @@ -364,8 +364,7 @@ fn handle_focus_keys( .filter(|e| { focusables .get(*e) - .map(|(_, disabled)| !disabled) - .unwrap_or(false) + .is_ok_and(|(_, disabled)| !disabled) }) .collect(); if !row_cycle.is_empty() @@ -466,12 +465,7 @@ fn handle_focus_keys( // Stable sort by `Focusable::order` so explicit priorities (e.g. // HUD spawn-order: 0..5) drive the cycle. The pre-sort by entity // index above is the tiebreaker for entries sharing an `order`. - group.sort_by_key(|e| { - focusables - .get(*e) - .map(|(f, _)| f.order) - .unwrap_or(i32::MAX) - }); + group.sort_by_key(|e| focusables.get(*e).map_or(i32::MAX, |(f, _)| f.order)); if group.is_empty() { // Still consume the key so the card-selection plugin doesn't diff --git a/solitaire_engine/src/ui_modal.rs b/solitaire_engine/src/ui_modal.rs index ff01f28..05ac682 100644 --- a/solitaire_engine/src/ui_modal.rs +++ b/solitaire_engine/src/ui_modal.rs @@ -409,8 +409,7 @@ pub fn apply_modal_enter_speed( ) { let speed = settings .as_ref() - .map(|s| s.0.animation_speed) - .unwrap_or(AnimSpeed::Normal); + .map_or(AnimSpeed::Normal, |s| s.0.animation_speed); for mut entering in &mut q { entering.duration = scaled_duration(MOTION_MODAL_SECS, speed); } diff --git a/solitaire_engine/src/weekly_goals_plugin.rs b/solitaire_engine/src/weekly_goals_plugin.rs index 1a0d6b4..b7dd006 100644 --- a/solitaire_engine/src/weekly_goals_plugin.rs +++ b/solitaire_engine/src/weekly_goals_plugin.rs @@ -121,9 +121,7 @@ fn evaluate_weekly_goals( /// Resolve a goal id to its description (used for toasts). pub fn weekly_goal_description(id: &str) -> String { - weekly_goal_by_id(id) - .map(|g| g.description.to_string()) - .unwrap_or_else(|| id.to_string()) + weekly_goal_by_id(id).map_or_else(|| id.to_string(), |g| g.description.to_string()) } #[cfg(test)] diff --git a/solitaire_server/src/error.rs b/solitaire_server/src/error.rs index 70e7f1e..22aeb04 100644 --- a/solitaire_server/src/error.rs +++ b/solitaire_server/src/error.rs @@ -51,8 +51,9 @@ pub enum AppError { impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, message) = match &self { - AppError::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()), - AppError::InvalidCredentials => (StatusCode::UNAUTHORIZED, self.to_string()), + AppError::Unauthorized | AppError::InvalidCredentials => { + (StatusCode::UNAUTHORIZED, self.to_string()) + } AppError::UsernameTaken => (StatusCode::CONFLICT, self.to_string()), AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()), AppError::Database(e) => {