55 lines
2 KiB
Rust
55 lines
2 KiB
Rust
|
|
//! The mutation pipeline contracts (GameKernel §2.2, K1–K3):
|
|||
|
|
//! command → validate → events → infallible fold.
|
|||
|
|
|
|||
|
|
use crate::ids::PlayerId;
|
|||
|
|
use serde::{Deserialize, Serialize};
|
|||
|
|
|
|||
|
|
/// Who issued a command (GameKernel K2).
|
|||
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
|||
|
|
pub enum Actor {
|
|||
|
|
Player(PlayerId),
|
|||
|
|
/// Runtime-driven steps (reveal, resolution ordering, round end).
|
|||
|
|
System,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// Typed, testable command rejections (GameKernel K3). Game packages add
|
|||
|
|
/// specific rejection kinds via `Game`-typed payloads in their own enums;
|
|||
|
|
/// this kernel-level enum carries the cross-game cases.
|
|||
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|||
|
|
pub enum Rejection {
|
|||
|
|
/// The command's `CommandId` was already applied (idempotency, K2).
|
|||
|
|
DuplicateCommand,
|
|||
|
|
/// The actor may not issue this command now (turn/window gating).
|
|||
|
|
NotAllowedNow,
|
|||
|
|
/// Game-specific rejection, stable machine-readable code + human text.
|
|||
|
|
Game { code: String, detail: String },
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
impl core::fmt::Display for Rejection {
|
|||
|
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|||
|
|
match self {
|
|||
|
|
Rejection::DuplicateCommand => write!(f, "duplicate command"),
|
|||
|
|
Rejection::NotAllowedNow => write!(f, "not allowed now"),
|
|||
|
|
Rejection::Game { code, detail } => write!(f, "{code}: {detail}"),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// An event-sourced aggregate (GameKernel K1): all validation happens in
|
|||
|
|
/// `validate`; `fold` is total and infallible for any logged event.
|
|||
|
|
pub trait Aggregate: Sized {
|
|||
|
|
type Command;
|
|||
|
|
type Event;
|
|||
|
|
|
|||
|
|
/// Decide whether `command` is legal in the current state, producing
|
|||
|
|
/// the events it implies. Must not mutate state.
|
|||
|
|
fn validate(
|
|||
|
|
&self,
|
|||
|
|
actor: Actor,
|
|||
|
|
command: &Self::Command,
|
|||
|
|
) -> Result<Vec<Self::Event>, Rejection>;
|
|||
|
|
|
|||
|
|
/// Apply one event. Infallible: every logged event must apply (K1).
|
|||
|
|
fn fold(&mut self, event: &Self::Event);
|
|||
|
|
}
|