From 791a0e48e3b54691c9f862e59f8648425de396fe Mon Sep 17 00:00:00 2001 From: Xevion Date: Mon, 29 Dec 2025 01:15:03 -0600 Subject: [PATCH] fix(lint): resolve clippy warnings and add cross-platform lint recipe - Add `just lint` recipe for desktop + wasm clippy checks - Fix clippy warnings: redundant field names, formatting, dead code - Gate Emscripten-specific audio methods behind target_os cfg --- Justfile | 8 ++++++++ pacman-server/src/app.rs | 2 +- pacman/src/audio.rs | 18 +++++++++++++++--- pacman/src/game.rs | 7 +++++-- pacman/src/platform/emscripten.rs | 7 +------ pacman/src/systems/hud/score.rs | 4 ++-- pacman/src/systems/state.rs | 15 ++++++++++++++- 7 files changed, 46 insertions(+), 15 deletions(-) diff --git a/Justfile b/Justfile index 31b1acc..e8bb450 100644 --- a/Justfile +++ b/Justfile @@ -40,6 +40,14 @@ web *args: bun run --cwd web build caddy file-server --root web/dist/client --listen :8547 +# Run strict multi-platform lints (desktop + wasm) +lint: + @echo "Running clippy for desktop target..." + @cargo clippy --all-targets --all-features --quiet -- -D warnings + @echo "Running clippy for wasm target..." + @cargo clippy -p pacman --target wasm32-unknown-emscripten --all-features --quiet -- -D warnings + @echo "All lints passed!" + # Fix linting errors & formatting fix: cargo fix --workspace --lib --allow-dirty diff --git a/pacman-server/src/app.rs b/pacman-server/src/app.rs index e0422b5..f0a61cf 100644 --- a/pacman-server/src/app.rs +++ b/pacman-server/src/app.rs @@ -75,7 +75,7 @@ impl AppState { sessions: Arc::new(DashMap::new()), jwt_encoding_key: Arc::new(EncodingKey::from_secret(jwt_secret.as_bytes())), jwt_decoding_key: Arc::new(DecodingKey::from_secret(jwt_secret.as_bytes())), - db: db, + db, health: Arc::new(RwLock::new(Health::default())), image_storage, healthchecker_task: Arc::new(RwLock::new(None)), diff --git a/pacman/src/audio.rs b/pacman/src/audio.rs index 7441459..53192b4 100644 --- a/pacman/src/audio.rs +++ b/pacman/src/audio.rs @@ -56,11 +56,18 @@ pub struct Audio { #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum AudioState { - Enabled { volume: u8 }, - Muted { previous_volume: u8 }, + Enabled { + volume: u8, + }, + Muted { + previous_volume: u8, + }, /// Audio is suspended until user interaction unlocks it (browser autoplay policy). /// On Emscripten, audio starts in this state and transitions to Enabled when unlock() is called. - Suspended { volume: u8 }, + #[cfg(target_os = "emscripten")] + Suspended { + volume: u8, + }, Disabled, } @@ -264,6 +271,7 @@ impl Audio { /// /// Transitions from Suspended to Enabled state, allowing audio to play. /// Called when the user clicks or presses a key to satisfy browser autoplay policy. + #[cfg(target_os = "emscripten")] pub fn unlock(&mut self) { if let AudioState::Suspended { volume } = self.state { tracing::info!("Audio unlocked after user interaction"); @@ -275,11 +283,15 @@ impl Audio { /// /// Returns `true` if audio is in the Enabled state, `false` if suspended, /// muted, or disabled. + #[cfg(target_os = "emscripten")] + #[allow(dead_code)] pub fn is_ready(&self) -> bool { matches!(self.state, AudioState::Enabled { .. }) } /// Returns whether audio is suspended waiting for user interaction. + #[cfg(target_os = "emscripten")] + #[allow(dead_code)] pub fn is_suspended(&self) -> bool { matches!(self.state, AudioState::Suspended { .. }) } diff --git a/pacman/src/game.rs b/pacman/src/game.rs index bc81bee..d630127 100644 --- a/pacman/src/game.rs +++ b/pacman/src/game.rs @@ -22,9 +22,12 @@ use crate::systems::{ EntityType, Frozen, FruitSprites, GameStage, Ghost, GhostAnimation, GhostAnimations, GhostBundle, GhostCollider, GhostState, GlobalState, ItemBundle, ItemCollider, LastAnimationState, LinearAnimation, MapTextureResource, MovementModifiers, NodeId, PacmanCollider, PlayerAnimation, PlayerBundle, PlayerControlled, PlayerDeathAnimation, PlayerLives, Position, RenderDirty, - Renderable, ScoreResource, StartupSequence, SystemId, SystemTimings, Timing, TouchState, Velocity, Visibility, + Renderable, ScoreResource, SystemId, SystemTimings, Timing, TouchState, Velocity, Visibility, }; +#[cfg(not(target_os = "emscripten"))] +use crate::systems::StartupSequence; + use crate::texture::animated::{DirectionalTiles, TileSequence}; use crate::texture::sprite::AtlasTile; use crate::texture::sprites::{FrightenedColor, GameSprite, GhostSprite, MazeSprite, PacmanSprite}; @@ -787,7 +790,7 @@ impl Game { // Use dt to determine expected frame time, with 80% as threshold to account for normal variance // Desktop uses LOOP_TIME (~16.67ms), WebAssembly adapts to requestAnimationFrame timing let frame_budget_ms = (dt * 1000.0 * 1.2) as u128; - + // Log performance warnings for slow frames if total_duration.as_millis() > frame_budget_ms { let slowest_systems = timings.get_slowest_systems(); diff --git a/pacman/src/platform/emscripten.rs b/pacman/src/platform/emscripten.rs index decf3af..ae201ce 100644 --- a/pacman/src/platform/emscripten.rs +++ b/pacman/src/platform/emscripten.rs @@ -23,12 +23,7 @@ extern "C" { /// - `arg`: user data pointer passed to callback /// - `fps`: target FPS (0 = use requestAnimationFrame) /// - `simulate_infinite_loop`: if 1, never returns (standard for games) - pub fn emscripten_set_main_loop_arg( - func: EmMainLoopCallback, - arg: *mut c_void, - fps: c_int, - simulate_infinite_loop: c_int, - ); + pub fn emscripten_set_main_loop_arg(func: EmMainLoopCallback, arg: *mut c_void, fps: c_int, simulate_infinite_loop: c_int); /// Execute JavaScript code from Rust fn emscripten_run_script(script: *const i8); diff --git a/pacman/src/systems/hud/score.rs b/pacman/src/systems/hud/score.rs index c6cae88..841b3b4 100644 --- a/pacman/src/systems/hud/score.rs +++ b/pacman/src/systems/hud/score.rs @@ -89,7 +89,7 @@ pub fn hud_render_system( if pause_state.active() { // Enable blending for transparency canvas.set_blend_mode(sdl2::render::BlendMode::Blend); - + // Draw semi-transparent black overlay canvas.set_draw_color(Color::RGBA(0, 0, 0, 160)); let _ = canvas.fill_rect(Rect::new(0, 0, constants::CANVAS_SIZE.x, constants::CANVAS_SIZE.y)); @@ -101,7 +101,7 @@ pub fn hud_render_system( let paused_height = paused_renderer.text_height(); let paused_position = glam::UVec2::new( (constants::CANVAS_SIZE.x - paused_width) / 2, - (constants::CANVAS_SIZE.y - paused_height) / 2 + (constants::CANVAS_SIZE.y - paused_height) / 2, ); if let Err(e) = paused_renderer.render_with_color(canvas, &mut atlas, paused_text, paused_position, Color::YELLOW) { errors.write(TextureError::RenderFailed(format!("Failed to render PAUSED text: {}", e)).into()); diff --git a/pacman/src/systems/state.rs b/pacman/src/systems/state.rs index 77a9bbe..299aa4e 100644 --- a/pacman/src/systems/state.rs +++ b/pacman/src/systems/state.rs @@ -43,6 +43,7 @@ pub struct IntroPlayed(pub bool); pub enum GameStage { /// Waiting for user interaction before starting (Emscripten only). /// Game is rendered but audio/gameplay are paused until the user clicks or presses a key. + #[cfg(target_os = "emscripten")] WaitingForInteraction, Starting(StartupSequence), /// The main gameplay loop is active. @@ -186,7 +187,15 @@ impl TooSimilar for GameStage { fn too_similar(&self, other: &Self) -> bool { discriminant(self) == discriminant(other) && { // These states are very simple, so they're 'too similar' automatically - if matches!(self, GameStage::Playing | GameStage::GameOver | GameStage::WaitingForInteraction) { + #[cfg(target_os = "emscripten")] + if matches!( + self, + GameStage::Playing | GameStage::GameOver | GameStage::WaitingForInteraction + ) { + return true; + } + #[cfg(not(target_os = "emscripten"))] + if matches!(self, GameStage::Playing | GameStage::GameOver) { return true; } @@ -210,7 +219,10 @@ impl TooSimilar for GameStage { }, ) => ghost_entity == other_ghost_entity && ghost_type == other_ghost_type && node == other_node, // Already handled, but kept to properly exhaust the match + #[cfg(target_os = "emscripten")] (GameStage::Playing, _) | (GameStage::GameOver, _) | (GameStage::WaitingForInteraction, _) => unreachable!(), + #[cfg(not(target_os = "emscripten"))] + (GameStage::Playing, _) | (GameStage::GameOver, _) => unreachable!(), _ => unreachable!(), } } @@ -315,6 +327,7 @@ pub fn stage_system( } let new_state: GameStage = new_state_opt.unwrap_or_else(|| match *game_state { + #[cfg(target_os = "emscripten")] GameStage::WaitingForInteraction => { // Stay in this state until JS calls start_game() *game_state