From 46202ee2e398ffd49febe6684f5c6a8648da600d Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 17:58:03 +0100 Subject: v4.0.1: introduce Command/View and port roulette to the shared boundary --- src/client/view.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/client/view.rs (limited to 'src/client/view.rs') diff --git a/src/client/view.rs b/src/client/view.rs new file mode 100644 index 0000000..3683ac2 --- /dev/null +++ b/src/client/view.rs @@ -0,0 +1,103 @@ +//! What a frontend draws and what it can ask the core to do. +//! +//! The core owns state and navigation; adapters map input to `Command` and +//! render `View`. Terminal text lives in [`crate::client::tui`], not here. + +use crate::client::games::roulette::{Cursor, Direction}; +use crate::client::games::traits::GameState; +use crate::roulette::{BetTarget, Bets}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RoulettePhase { + Placing, + Spinning, + Result, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RouletteView { + pub bet_amount: u64, + pub balance: Option, + pub chip: u64, + pub bets: Bets, + pub cursor: Cursor, + pub phase: RoulettePhase, + pub number: Option, + pub color: Option, + pub winnings: Option, + pub error: Option, +} + +impl RouletteView { + pub fn from_state(state: &GameState) -> Self { + let bets = state + .data + .get("bets") + .and_then(|raw| Bets::from_json(raw).ok()) + .unwrap_or_default(); + let chip = state + .data + .get("chip") + .and_then(|value| value.parse().ok()) + .unwrap_or(10); + let cursor = state + .data + .get("cursor") + .map(|raw| Cursor::parse(raw)) + .unwrap_or_default(); + let phase = match state.data.get("status").map(String::as_str) { + Some("spinning") => RoulettePhase::Spinning, + Some("result") => RoulettePhase::Result, + _ => RoulettePhase::Placing, + }; + let number = state.data.get("number").and_then(|value| value.parse().ok()); + let color = state.data.get("color").cloned(); + let winnings = state + .data + .get("winnings") + .and_then(|value| value.parse().ok()); + let balance = state + .data + .get("balance") + .and_then(|value| value.parse().ok()); + let error = state + .data + .get("error") + .cloned() + .filter(|message| !message.is_empty()); + + Self { + bet_amount: state.bet_amount, + balance, + chip, + bets, + cursor, + phase, + number, + color, + winnings, + error, + } + } +} + +/// Everything a frontend can draw. `Text` is the bridge for games that have +/// not been ported to a structured view yet. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum View { + Roulette(RouletteView), + Text(Vec), +} + +/// Semantic input. Frontends turn keys and clicks into these. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Command { + MoveCursor(Direction), + PlaceBet, + PlaceBetAt(BetTarget), + ClearBets, + SetChip(u64), + AdjustChip(i64), + Spin, + Leave, +} -- cgit v1.2.3