clay-borg/crates/cb-kernel/src/pipeline.rs

54 lines
2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The mutation pipeline contracts (GameKernel §2.2, K1K3):
//! 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);
}