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
+29 -29
View File
@@ -483,7 +483,7 @@ impl Plugin for CardPlugin {
update_stock_empty_indicator.after(GameMutation),
update_stock_count_badge
.after(GameMutation)
.run_if(resource_changed::<crate::GameStateResource>),
.run_if(resource_changed::<GameStateResource>),
collect_resize_events.after(LayoutSystem::UpdateOnResize),
snap_cards_on_window_resize.after(collect_resize_events),
),
@@ -2431,7 +2431,7 @@ fn update_tableau_fan_frac(
.into_iter()
.map(|tableau| {
game.0
.pile(solitaire_core::KlondikePile::Tableau(tableau))
.pile(KlondikePile::Tableau(tableau))
.into_iter()
.filter(|(_, face_up)| *face_up)
.count()
@@ -2566,7 +2566,7 @@ mod tests {
#[test]
fn card_positions_includes_all_52_cards_at_game_start() {
// At game start waste is empty, so all 52 cards are across stock + tableau.
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let g = GameState::new(42, DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
assert_eq!(positions.len(), 52);
@@ -2580,7 +2580,7 @@ mod tests {
for _ in 0..3 {
let _ = g.draw();
}
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
g.waste_cards().iter().map(|c| c.0.clone()).collect();
assert_eq!(waste_ids.len(), 3);
@@ -2624,7 +2624,7 @@ mod tests {
"need at least 3 waste cards for this test"
);
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
waste_pile.iter().map(|c| c.0.clone()).collect();
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
@@ -2677,7 +2677,7 @@ mod tests {
let count = waste_pile.len();
assert!(count >= 2, "need at least 2 waste cards");
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
waste_pile.iter().map(|c| c.0.clone()).collect();
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -2715,7 +2715,7 @@ mod tests {
for _ in 0..3 {
let _ = g.draw();
}
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
g.waste_cards().iter().map(|c| c.0.clone()).collect();
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -2740,7 +2740,7 @@ mod tests {
#[test]
fn card_positions_tableau_cards_are_fanned_downward() {
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let g = GameState::new(42, DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -3083,7 +3083,7 @@ mod tests {
#[test]
fn facedown_cards_use_tighter_fan_than_uniform_faceup_fan() {
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let g = GameState::new(42, DrawStockConfig::DrawOne);
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
@@ -3153,7 +3153,7 @@ mod tests {
fn fire_window_resize(app: &mut App, width: f32, height: f32) {
// Any Entity will do — the snap system reads only width/height.
let window = bevy::ecs::entity::Entity::from_raw_u32(0)
let window = Entity::from_raw_u32(0)
.expect("Entity::from_raw_u32(0) is a valid placeholder");
app.world_mut().write_message(WindowResized {
window,
@@ -3171,9 +3171,9 @@ mod tests {
// entity IDs must remain alive.
let mut app = app();
let labels_before: std::collections::HashSet<bevy::prelude::Entity> = app
let labels_before: HashSet<Entity> = app
.world_mut()
.query_filtered::<bevy::prelude::Entity, With<CardLabel>>()
.query_filtered::<Entity, With<CardLabel>>()
.iter(app.world())
.collect();
assert!(
@@ -3184,9 +3184,9 @@ mod tests {
fire_window_resize(&mut app, 1024.0, 768.0);
advance_past_resize_throttle(&mut app);
let labels_after: std::collections::HashSet<bevy::prelude::Entity> = app
let labels_after: HashSet<Entity> = app
.world_mut()
.query_filtered::<bevy::prelude::Entity, With<CardLabel>>()
.query_filtered::<Entity, With<CardLabel>>()
.iter(app.world())
.collect();
@@ -3336,9 +3336,9 @@ mod tests {
// Each shadow's parent must be a CardEntity, so the child relation
// is wired correctly.
let cards: HashSet<bevy::prelude::Entity> = app
let cards: HashSet<Entity> = app
.world_mut()
.query_filtered::<bevy::prelude::Entity, With<CardEntity>>()
.query_filtered::<Entity, With<CardEntity>>()
.iter(app.world())
.collect();
let mut q = app
@@ -3423,7 +3423,7 @@ mod tests {
let card_entity = {
let mut q = app
.world_mut()
.query::<(bevy::prelude::Entity, &CardEntity)>();
.query::<(Entity, &CardEntity)>();
q.iter(app.world())
.find(|(_, c)| c.card == *card)
.map(|(e, _)| e)
@@ -3533,7 +3533,7 @@ mod tests {
#[test]
fn stock_card_count_helper_reads_zero_for_empty_stock() {
let g = GameState::new(42, solitaire_core::DrawStockConfig::DrawOne);
let g = GameState::new(42, DrawStockConfig::DrawOne);
let mut g_empty_stock = g.clone();
g_empty_stock.set_test_stock_cards(Vec::new());
assert_eq!(stock_card_count(&g_empty_stock), 0);
@@ -3553,9 +3553,9 @@ mod tests {
// Allocate five different strong handles by passing each a
// distinct dummy `Image`. We never render these; we only
// compare ids.
let mut images = bevy::asset::Assets::<bevy::image::Image>::default();
let backs: [Handle<bevy::image::Image>; 5] =
std::array::from_fn(|_| images.add(bevy::image::Image::default()));
let mut images = Assets::<Image>::default();
let backs: [Handle<Image>; 5] =
std::array::from_fn(|_| images.add(Image::default()));
CardImageSet {
faces: std::array::from_fn(|_| std::array::from_fn(|_| Handle::default())),
backs,
@@ -3569,8 +3569,8 @@ mod tests {
// card must render with the theme's back regardless of which
// legacy back the player picked in Settings.
let mut set = image_set_with_distinct_back_handles();
let mut images = bevy::asset::Assets::<bevy::image::Image>::default();
let theme_back: Handle<bevy::image::Image> = images.add(bevy::image::Image::default());
let mut images = Assets::<Image>::default();
let theme_back: Handle<Image> = images.add(Image::default());
set.theme_back = Some(theme_back.clone());
let face_down = make_card(Suit::Spades, Rank::Ace);
@@ -3634,8 +3634,8 @@ mod tests {
use std::collections::HashMap;
let mut set = image_set_with_distinct_back_handles();
let mut images = bevy::asset::Assets::<bevy::image::Image>::default();
let theme_back: Handle<bevy::image::Image> = images.add(bevy::image::Image::default());
let mut images = Assets::<Image>::default();
let theme_back: Handle<Image> = images.add(Image::default());
let theme = CardTheme {
meta: ThemeMeta {
@@ -3645,7 +3645,7 @@ mod tests {
version: "0".into(),
card_aspect: (2, 3),
},
faces: HashMap::<CardKey, Handle<bevy::image::Image>>::new(),
faces: HashMap::<CardKey, Handle<Image>>::new(),
back: theme_back.clone(),
};
@@ -3813,7 +3813,7 @@ mod tests {
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
g.waste_cards().iter().map(|c| c.0.clone()).collect();
let mut waste_zs: Vec<f32> = positions
@@ -3863,7 +3863,7 @@ mod tests {
let stock_x = layout.pile_positions[&KlondikePile::Stock].x;
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
g.waste_cards().iter().map(|c| c.0.clone()).collect();
let mut waste_positions: Vec<_> = card_positions(&g, &layout)
@@ -3893,7 +3893,7 @@ mod tests {
let layout = crate::layout::compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
let positions = card_positions(&g, &layout);
let waste_ids: std::collections::HashSet<Card> =
let waste_ids: HashSet<Card> =
g.waste_cards().iter().map(|c| c.0.clone()).collect();
let mut waste_zs: Vec<f32> = positions