chore: add pedantic workspace lints (#90)

Add [workspace.lints.rust] and wire each member crate up with
[lints] workspace = true:

  unsafe_code = "deny"        (forbid would break the Android JNI build)
  single_use_lifetimes = "warn"
  trivial_casts = "warn"
  unused_lifetimes = "warn"
  unused_qualifications = "warn"
  variant_size_differences = "warn"
  unexpected_cfgs = "warn"

unsafe_code is "deny" rather than the issue's "forbid" so the three
Android JNI FFI modules (android_keystore, android_clipboard, safe_area)
can opt back in with a scoped #![allow(unsafe_code)] — forbid cannot be
locally overridden. Pure crates carry no unsafe and stay clean.

Clean up the warnings the new lints surface:
- 150ish unused_qualifications removed via `cargo fix` (purely syntactic
  redundant-path-prefix removals).
- table_plugin: the TABLE_COLOUR import was #[cfg(test)]-gated while the
  camera clear-colour used the fully-qualified path; unqualifying it left
  a non-test build with no import. Made the import unconditional instead.
- assets/sources: the `as &[u8]` casts in embed_*_svg! coerce each
  fixed-size &[u8; N] to a uniform slice so the tuples fit the
  &[(&str, &[u8])] arrays — load-bearing, so scoped #[allow(trivial_casts)].

Workspace clippy -D warnings and the full test suite pass. Android build
not compiled here (needs the NDK; built separately per CLAUDE.md §15) —
the deny + scoped-allow keeps the JNI unsafe blocks legal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-12 13:05:28 -07:00
parent 9bbb57134f
commit ceb9c950a1
48 changed files with 191 additions and 130 deletions
+6 -6
View File
@@ -201,7 +201,7 @@ impl Plugin for GamePlugin {
.add_message::<StateChangedEvent>()
.add_message::<crate::events::MoveRejectedEvent>()
.add_message::<GameWonEvent>()
.add_message::<crate::events::CardFlippedEvent>()
.add_message::<CardFlippedEvent>()
.add_message::<crate::events::AchievementUnlockedEvent>()
.add_message::<FoundationCompletedEvent>()
.add_message::<InfoToastEvent>()
@@ -530,7 +530,7 @@ fn handle_new_game(
// hides that information and reads naturally as "dealt from the
// deck." Skipped when LayoutResource isn't present (headless tests).
if let Some(layout) = layout.as_ref()
&& let Some(stock) = layout.0.pile_positions.get(&solitaire_core::KlondikePile::Stock)
&& let Some(stock) = layout.0.pile_positions.get(&KlondikePile::Stock)
{
for mut tx in &mut card_transforms {
tx.translation.x = stock.x;
@@ -868,7 +868,7 @@ fn handle_move(
mut game: ResMut<GameStateResource>,
mut changed: MessageWriter<StateChangedEvent>,
mut won: MessageWriter<GameWonEvent>,
mut flipped: MessageWriter<crate::events::CardFlippedEvent>,
mut flipped: MessageWriter<CardFlippedEvent>,
mut foundation_done: MessageWriter<FoundationCompletedEvent>,
mut recording: ResMut<RecordingReplay>,
path: Option<Res<GameStatePath>>,
@@ -909,7 +909,7 @@ fn handle_move(
.last()
.is_some_and(|c| c.0 == fcard && c.1)
{
flipped.write(crate::events::CardFlippedEvent(fcard));
flipped.write(CardFlippedEvent(fcard));
}
// If this move landed on a foundation pile and that pile is
// now complete (Ace → King, 13 cards), fire the per-suit
@@ -1530,7 +1530,7 @@ mod tests {
// Persistence tests
// -----------------------------------------------------------------------
fn tmp_gs_path(name: &str) -> std::path::PathBuf {
fn tmp_gs_path(name: &str) -> PathBuf {
std::env::temp_dir().join(format!("engine_test_gs_{name}.json"))
}
@@ -1684,7 +1684,7 @@ mod tests {
let events = app
.world()
.resource::<Messages<crate::events::CardFlippedEvent>>();
.resource::<Messages<CardFlippedEvent>>();
let mut cursor = events.get_cursor();
let fired: Vec<_> = cursor.read(events).collect();
assert!(