Skip to main content

whycodes_session/
stream_rules.rs

1//! Which stream rules fired in this session, for `repeat` limits.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7/// User-turn counter plus the turn each stream rule last fired on.
8#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
9pub struct StreamRuleLedger {
10    /// User turns started in this session (1-based once a turn runs).
11    #[serde(default)]
12    pub turn: u64,
13    /// Rule name → `turn` of its last hit.
14    #[serde(default)]
15    pub fired: BTreeMap<String, u64>,
16}
17
18impl StreamRuleLedger {
19    /// Nothing fired yet (the turn counter alone is not worth persisting).
20    pub fn is_empty(&self) -> bool {
21        self.fired.is_empty()
22    }
23
24    /// A new user turn starts.
25    pub fn begin_turn(&mut self) {
26        self.turn = self.turn.saturating_add(1);
27    }
28
29    /// Turn on which `name` last fired.
30    pub fn last_fired(&self, name: &str) -> Option<u64> {
31        self.fired.get(name).copied()
32    }
33
34    /// Record a hit of `name` on the current turn.
35    pub fn record(&mut self, name: &str) {
36        self.fired.insert(name.to_string(), self.turn);
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn ledger_tracks_turns_and_hits() {
46        let mut l = StreamRuleLedger::default();
47        assert!(l.is_empty());
48        l.begin_turn();
49        l.begin_turn();
50        assert_eq!(l.last_fired("a"), None);
51        l.record("a");
52        assert!(!l.is_empty());
53        assert_eq!(l.last_fired("a"), Some(2));
54        let back: StreamRuleLedger = serde_json::from_str("{}").unwrap();
55        assert_eq!(back, StreamRuleLedger::default());
56    }
57}