vera-effects 0.1.1

VERA (Verified Effect-Rule Architecture) — generic trace, rule output, and reaction types for building verifiable game loops where every state mutation is inspectable and testable.
Documentation
//! # VERA — Verified Effect-Rule Architecture
//!
//! Generic infrastructure for effect-verified game loops.
//!
//! VERA's core idea: pure rule functions return effect enums, a mechanical
//! apply function mutates state, and a trace records everything that happened
//! for verification.
//!
//! This crate provides the generic types. Your game crate defines the concrete
//! `Effect` and `Presentation` enums and uses these types parameterized over them.
//!
//! # Example
//!
//! ```rust
//! use vera_effects::{Trace, TraceEntry, TraceSource, RuleOutput};
//!
//! // Your game defines concrete effect types
//! #[derive(Debug, Clone, PartialEq)]
//! enum Effect {
//!     Heal { amount: i32 },
//!     SpendAp { amount: i32 },
//! }
//!
//! #[derive(Debug, Clone)]
//! enum Presentation {
//!     LogMessage(String),
//! }
//!
//! // Rule functions return RuleOutput<Effect, Presentation>
//! fn rule_heal(amount: i32) -> RuleOutput<Effect, Presentation> {
//!     RuleOutput {
//!         effects: vec![Effect::Heal { amount }],
//!         presentation: vec![Presentation::LogMessage(format!("+{} HP", amount))],
//!     }
//! }
//!
//! // Trace records what happened
//! let mut trace = Trace::<Effect>::default();
//! trace.enabled = true;
//! let output = rule_heal(25);
//! for effect in &output.effects {
//!     trace.record(effect, TraceSource::Rule { name: "rule_heal" }, 1);
//! }
//! assert!(trace.contains(&Effect::Heal { amount: 25 }));
//! ```

use std::fmt;

// ---------------------------------------------------------------------------
// Trace — records effects for verification
// ---------------------------------------------------------------------------

/// A single entry in the trace log.
#[derive(Debug, Clone)]
pub struct TraceEntry<E: Clone> {
    pub turn: u32,
    pub source: TraceSource<E>,
    pub effect: E,
}

/// Where an effect came from.
#[derive(Debug, Clone)]
pub enum TraceSource<E: Clone> {
    /// Produced by a rule function.
    Rule { name: &'static str },
    /// Produced by a reaction to another effect.
    Reaction { name: &'static str, trigger: Box<E> },
}

/// Ordered record of all effects. Only populated when `enabled` is true.
/// Ephemeral — not serialized, not saved.
#[derive(Clone)]
pub struct Trace<E: Clone + PartialEq> {
    pub entries: Vec<TraceEntry<E>>,
    pub enabled: bool,
}

impl<E: Clone + PartialEq> Default for Trace<E> {
    fn default() -> Self {
        Self {
            entries: Vec::new(),
            enabled: false,
        }
    }
}

impl<E: Clone + PartialEq> Trace<E> {
    /// Record an effect if tracing is enabled.
    pub fn record(&mut self, effect: &E, source: TraceSource<E>, turn: u32) {
        if self.enabled {
            self.entries.push(TraceEntry {
                turn,
                source,
                effect: effect.clone(),
            });
        }
    }

    /// Check if a specific effect was recorded.
    pub fn contains(&self, effect: &E) -> bool {
        self.entries.iter().any(|e| &e.effect == effect)
    }

    /// All effects from a specific rule.
    pub fn from_rule(&self, name: &str) -> Vec<&E> {
        self.entries
            .iter()
            .filter(|e| matches!(&e.source, TraceSource::Rule { name: n } if *n == name))
            .map(|e| &e.effect)
            .collect()
    }

    /// All effects matching a predicate.
    pub fn effects_matching<F>(&self, predicate: F) -> Vec<&E>
    where
        F: Fn(&E) -> bool,
    {
        self.entries
            .iter()
            .filter(|e| predicate(&e.effect))
            .map(|e| &e.effect)
            .collect()
    }
}

impl<E: Clone + PartialEq + fmt::Debug> fmt::Debug for Trace<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Trace")
            .field("enabled", &self.enabled)
            .field("entries", &self.entries.len())
            .finish()
    }
}

impl<E: Clone + PartialEq + fmt::Debug> fmt::Display for Trace<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for entry in &self.entries {
            writeln!(
                f,
                "  Turn {}: [{:?}] {:?}",
                entry.turn, entry.source, entry.effect
            )?;
        }
        Ok(())
    }
}

// ---------------------------------------------------------------------------
// RuleOutput — what a rule function returns
// ---------------------------------------------------------------------------

/// The output of a rule function: game effects to apply + presentation effects.
#[derive(Debug, Clone)]
pub struct RuleOutput<E, P> {
    pub effects: Vec<E>,
    pub presentation: Vec<P>,
}

impl<E, P> Default for RuleOutput<E, P> {
    fn default() -> Self {
        Self {
            effects: Vec::new(),
            presentation: Vec::new(),
        }
    }
}