refactor: replace local DrawMode with upstream klondike::DrawStockConfig (#82)

DrawMode was a 1:1 mirror of klondike::DrawStockConfig (DrawOne/DrawThree).
Delete it and use the upstream type everywhere; re-export DrawStockConfig from
solitaire_core. config_for assigns draw_stock directly and draw_mode() returns
session.config().inner.draw_stock.

Serde is unchanged — DrawStockConfig serialises to the same "DrawOne"/"DrawThree"
named variants, so persisted game_state.json / replay JSON stay byte-compatible
(no migration). Field/method/variable names containing draw_mode are unchanged.

35 files, mechanical type swap across all crates. Implemented via a multi-agent
workflow (core → per-crate consumers → verify). cargo test --workspace and
clippy --workspace --all-targets -- -D warnings green.

Closes #82

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-11 16:01:11 -07:00
parent d045781119
commit 5c992cbdca
35 changed files with 257 additions and 274 deletions
+26 -26
View File
@@ -2,10 +2,10 @@
//!
//! [`StatsSnapshot`] is defined in `solitaire_sync` and re-exported here.
//! This module adds the [`StatsExt`] extension trait, which supplies the
//! `update_on_win` method that depends on [`DrawMode`] from `solitaire_core`.
//! `update_on_win` method that depends on [`DrawStockConfig`] from `solitaire_core`.
use chrono::Utc;
use solitaire_core::{DrawMode, game_state::GameMode};
use solitaire_core::{DrawStockConfig, game_state::GameMode};
pub use solitaire_sync::StatsSnapshot;
@@ -18,9 +18,9 @@ pub trait StatsExt {
///
/// Tracks lifetime totals only — per-mode best scores and times are
/// updated separately via [`StatsExt::update_per_mode_bests`] so the
/// long-standing call sites that only know about [`DrawMode`] keep
/// long-standing call sites that only know about [`DrawStockConfig`] keep
/// compiling.
fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawMode);
fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawStockConfig);
/// Updates the per-mode best score and fastest-win-time fields for the
/// given [`GameMode`]. Call alongside [`StatsExt::update_on_win`] from
@@ -37,7 +37,7 @@ pub trait StatsExt {
}
impl StatsExt for StatsSnapshot {
fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawMode) {
fn update_on_win(&mut self, score: i32, time_seconds: u64, draw_mode: &DrawStockConfig) {
let prev_wins = self.games_won;
self.games_played += 1;
self.games_won += 1;
@@ -64,8 +64,8 @@ impl StatsExt for StatsSnapshot {
};
match draw_mode {
DrawMode::DrawOne => self.draw_one_wins += 1,
DrawMode::DrawThree => self.draw_three_wins += 1,
DrawStockConfig::DrawOne => self.draw_one_wins += 1,
DrawStockConfig::DrawThree => self.draw_three_wins += 1,
}
self.last_modified = Utc::now();
@@ -135,7 +135,7 @@ mod tests {
#[test]
fn first_win_sets_all_fields() {
let mut s = StatsSnapshot::default();
s.update_on_win(1500, 120, &DrawMode::DrawOne);
s.update_on_win(1500, 120, &DrawStockConfig::DrawOne);
assert_eq!(s.games_played, 1);
assert_eq!(s.games_won, 1);
assert_eq!(s.win_streak_current, 1);
@@ -152,7 +152,7 @@ mod tests {
fn streak_tracks_across_wins() {
let mut s = StatsSnapshot::default();
for _ in 0..3 {
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
}
assert_eq!(s.win_streak_current, 3);
assert_eq!(s.win_streak_best, 3);
@@ -161,8 +161,8 @@ mod tests {
#[test]
fn record_abandoned_resets_streak_and_increments_played() {
let mut s = StatsSnapshot::default();
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
assert_eq!(s.win_streak_current, 2);
s.record_abandoned();
assert_eq!(s.games_played, 3);
@@ -174,35 +174,35 @@ mod tests {
#[test]
fn fastest_win_takes_minimum() {
let mut s = StatsSnapshot::default();
s.update_on_win(100, 300, &DrawMode::DrawOne);
s.update_on_win(100, 120, &DrawMode::DrawOne);
s.update_on_win(100, 500, &DrawMode::DrawOne);
s.update_on_win(100, 300, &DrawStockConfig::DrawOne);
s.update_on_win(100, 120, &DrawStockConfig::DrawOne);
s.update_on_win(100, 500, &DrawStockConfig::DrawOne);
assert_eq!(s.fastest_win_seconds, 120);
}
#[test]
fn avg_time_is_correct_rolling_average() {
let mut s = StatsSnapshot::default();
s.update_on_win(100, 100, &DrawMode::DrawOne);
s.update_on_win(100, 200, &DrawMode::DrawOne);
s.update_on_win(100, 300, &DrawMode::DrawOne);
s.update_on_win(100, 100, &DrawStockConfig::DrawOne);
s.update_on_win(100, 200, &DrawStockConfig::DrawOne);
s.update_on_win(100, 300, &DrawStockConfig::DrawOne);
assert_eq!(s.avg_time_seconds, 200);
}
#[test]
fn best_score_updates_only_on_higher_score() {
let mut s = StatsSnapshot::default();
s.update_on_win(500, 60, &DrawMode::DrawOne);
s.update_on_win(300, 60, &DrawMode::DrawOne);
s.update_on_win(500, 60, &DrawStockConfig::DrawOne);
s.update_on_win(300, 60, &DrawStockConfig::DrawOne);
assert_eq!(s.best_single_score, 500);
s.update_on_win(800, 60, &DrawMode::DrawOne);
s.update_on_win(800, 60, &DrawStockConfig::DrawOne);
assert_eq!(s.best_single_score, 800);
}
#[test]
fn negative_score_treated_as_zero() {
let mut s = StatsSnapshot::default();
s.update_on_win(-50, 60, &DrawMode::DrawOne);
s.update_on_win(-50, 60, &DrawStockConfig::DrawOne);
assert_eq!(s.best_single_score, 0);
assert_eq!(s.lifetime_score, 0);
}
@@ -210,8 +210,8 @@ mod tests {
#[test]
fn draw_three_wins_tracked_separately() {
let mut s = StatsSnapshot::default();
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawMode::DrawThree);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawThree);
assert_eq!(s.draw_one_wins, 1);
assert_eq!(s.draw_three_wins, 1);
}
@@ -221,7 +221,7 @@ mod tests {
let mut s = StatsSnapshot::default();
// Build a streak of 5.
for _ in 0..5 {
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
}
assert_eq!(s.win_streak_best, 5);
// Lose (abandon), resetting current.
@@ -229,7 +229,7 @@ mod tests {
assert_eq!(s.win_streak_current, 0);
assert_eq!(s.win_streak_best, 5, "best must survive the loss");
// Win once — current becomes 1, best must remain 5.
s.update_on_win(100, 60, &DrawMode::DrawOne);
s.update_on_win(100, 60, &DrawStockConfig::DrawOne);
assert_eq!(s.win_streak_current, 1);
assert_eq!(
s.win_streak_best, 5,
@@ -243,7 +243,7 @@ mod tests {
lifetime_score: u64::MAX - 100,
..Default::default()
};
s.update_on_win(200, 60, &DrawMode::DrawOne);
s.update_on_win(200, 60, &DrawStockConfig::DrawOne);
assert_eq!(
s.lifetime_score,
u64::MAX,