refactor(core,engine,wasm): canonical FOUNDATIONS/TABLEAUS consts; adopt upstream SUITS/RANKS
Review findings 1+2 (Quat-underuse lens, 2026-07-06):
- solitaire_core gains pub const FOUNDATIONS / TABLEAUS — the canonical
iteration source for the upstream pile enums (upstream klondike has no
Foundation::ALL, and inherent impls cannot be added to foreign types).
Deletes three identical private const-fn copies (radial_menu,
table_plugin, input_plugin) and the hand-enumerated variants in
card_plugin::sync::all_cards and solitaire_wasm.
- Hand-rolled [Suit; 4] / [Rank; 13] arrays replaced with upstream
Suit::SUITS / Rank::RANKS. The order-sensitive CardImageSet indexing
is re-keyed through canonical card_plugin::{suit_index, rank_index}
helpers that match upstream order, with regression tests asserting
the correspondence — one ordering everywhere instead of three
divergent local ones.
Net -177 lines. No behaviour change; all consumers go through the
canonical helpers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,22 +62,10 @@ impl CardKey {
|
||||
/// Iterator over all 52 valid keys, in suit-major / rank-ascending order.
|
||||
/// Used to enumerate the manifest's required entries.
|
||||
pub fn all() -> impl Iterator<Item = CardKey> {
|
||||
const SUITS: [Suit; 4] = [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades];
|
||||
const RANKS: [Rank; 13] = [
|
||||
Rank::Ace,
|
||||
Rank::Two,
|
||||
Rank::Three,
|
||||
Rank::Four,
|
||||
Rank::Five,
|
||||
Rank::Six,
|
||||
Rank::Seven,
|
||||
Rank::Eight,
|
||||
Rank::Nine,
|
||||
Rank::Ten,
|
||||
Rank::Jack,
|
||||
Rank::Queen,
|
||||
Rank::King,
|
||||
];
|
||||
// Order-independent enumeration — consumers check completeness and
|
||||
// round-trips, never positions.
|
||||
const SUITS: [Suit; 4] = Suit::SUITS;
|
||||
const RANKS: [Rank; 13] = Rank::RANKS;
|
||||
SUITS
|
||||
.into_iter()
|
||||
.flat_map(|s| RANKS.into_iter().map(move |r| CardKey::new(s, r)))
|
||||
|
||||
@@ -17,7 +17,7 @@ use solitaire_core::{Rank, Suit};
|
||||
use crate::assets::{
|
||||
bundled_theme_url, classic_theme_svg_bytes, dark_theme_svg_bytes, rasterize_svg, user_theme_dir,
|
||||
};
|
||||
use crate::card_plugin::CardImageSet;
|
||||
use crate::card_plugin::{CardImageSet, rank_index, suit_index};
|
||||
use crate::events::StateChangedEvent;
|
||||
|
||||
use super::loader::CardThemeLoader;
|
||||
@@ -242,22 +242,8 @@ fn sync_card_image_set_with_active_theme(
|
||||
/// `theme_back` when present, so writing here is sufficient to make
|
||||
/// every face-down card pick up the theme's art on the next sync.
|
||||
fn apply_theme_to_card_image_set(theme: &CardTheme, image_set: &mut CardImageSet) {
|
||||
for suit in [Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades] {
|
||||
for rank in [
|
||||
Rank::Ace,
|
||||
Rank::Two,
|
||||
Rank::Three,
|
||||
Rank::Four,
|
||||
Rank::Five,
|
||||
Rank::Six,
|
||||
Rank::Seven,
|
||||
Rank::Eight,
|
||||
Rank::Nine,
|
||||
Rank::Ten,
|
||||
Rank::Jack,
|
||||
Rank::Queen,
|
||||
Rank::King,
|
||||
] {
|
||||
for suit in Suit::SUITS {
|
||||
for rank in Rank::RANKS {
|
||||
if let Some(handle) = theme.faces.get(&CardKey::new(suit, rank)) {
|
||||
image_set.faces[suit_index(suit)][rank_index(rank)] = handle.clone();
|
||||
}
|
||||
@@ -266,36 +252,7 @@ fn apply_theme_to_card_image_set(theme: &CardTheme, image_set: &mut CardImageSet
|
||||
image_set.theme_back = Some(theme.back.clone());
|
||||
}
|
||||
|
||||
/// Index used by [`CardImageSet::faces`] for a given suit. Mirrors
|
||||
/// the `card_plugin` doc comment: Clubs=0, Diamonds=1, Hearts=2, Spades=3.
|
||||
const fn suit_index(s: Suit) -> usize {
|
||||
match s {
|
||||
Suit::Clubs => 0,
|
||||
Suit::Diamonds => 1,
|
||||
Suit::Hearts => 2,
|
||||
Suit::Spades => 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Index used by [`CardImageSet::faces`] for a given rank.
|
||||
/// Ace=0, Two=1 … King=12.
|
||||
const fn rank_index(r: Rank) -> usize {
|
||||
match r {
|
||||
Rank::Ace => 0,
|
||||
Rank::Two => 1,
|
||||
Rank::Three => 2,
|
||||
Rank::Four => 3,
|
||||
Rank::Five => 4,
|
||||
Rank::Six => 5,
|
||||
Rank::Seven => 6,
|
||||
Rank::Eight => 7,
|
||||
Rank::Nine => 8,
|
||||
Rank::Ten => 9,
|
||||
Rank::Jack => 10,
|
||||
Rank::Queen => 11,
|
||||
Rank::King => 12,
|
||||
}
|
||||
}
|
||||
|
||||
/// Switches the active theme to the one served at
|
||||
/// `themes://<theme_id>/theme.ron`. Returns the new `Handle<CardTheme>`
|
||||
@@ -446,21 +403,17 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suit_index_ranges_match_card_plugin_layout() {
|
||||
assert_eq!(suit_index(Suit::Clubs), 0);
|
||||
assert_eq!(suit_index(Suit::Diamonds), 1);
|
||||
assert_eq!(suit_index(Suit::Hearts), 2);
|
||||
assert_eq!(suit_index(Suit::Spades), 3);
|
||||
fn suit_index_matches_upstream_suits_order() {
|
||||
for (i, s) in Suit::SUITS.iter().enumerate() {
|
||||
assert_eq!(suit_index(*s), i, "faces outer layout = Suit::SUITS order");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rank_index_starts_at_ace_zero_and_ends_at_king_twelve() {
|
||||
assert_eq!(rank_index(Rank::Ace), 0);
|
||||
assert_eq!(rank_index(Rank::Two), 1);
|
||||
assert_eq!(rank_index(Rank::Ten), 9);
|
||||
assert_eq!(rank_index(Rank::Jack), 10);
|
||||
assert_eq!(rank_index(Rank::Queen), 11);
|
||||
assert_eq!(rank_index(Rank::King), 12);
|
||||
fn rank_index_matches_upstream_ranks_order() {
|
||||
for (i, r) in Rank::RANKS.iter().enumerate() {
|
||||
assert_eq!(rank_index(*r), i, "faces inner layout = Rank::RANKS order");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user