1use super::JsonValue;
2use super::json_to_string;
3use super::session::{EffectRecord, RecordedOutcome};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
6pub enum EffectReplayMode {
7 #[default]
8 Normal,
9 Record,
10 Replay,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum ReplayFailure {
15 Exhausted {
16 effect_type: String,
17 position: usize,
18 },
19 Mismatch {
20 seq: u32,
21 expected: String,
22 got: String,
23 },
24 ArgsMismatch {
25 seq: u32,
26 effect_type: String,
27 expected: String,
28 got: String,
29 },
30 Unconsumed {
31 remaining: usize,
32 },
33}
34
35#[derive(Debug, Clone, Default)]
36pub struct EffectReplayState {
37 mode: EffectReplayMode,
38 recorded_effects: Vec<EffectRecord>,
39 replay_effects: Vec<EffectRecord>,
40 replay_pos: usize,
41 validate_replay_args: bool,
42}
43
44impl EffectReplayState {
45 pub fn mode(&self) -> EffectReplayMode {
46 self.mode
47 }
48
49 pub fn set_normal(&mut self) {
50 self.mode = EffectReplayMode::Normal;
51 self.recorded_effects.clear();
52 self.replay_effects.clear();
53 self.replay_pos = 0;
54 self.validate_replay_args = false;
55 }
56
57 pub fn start_recording(&mut self) {
58 self.mode = EffectReplayMode::Record;
59 self.recorded_effects.clear();
60 self.replay_effects.clear();
61 self.replay_pos = 0;
62 self.validate_replay_args = false;
63 }
64
65 pub fn start_replay(&mut self, effects: Vec<EffectRecord>, validate_args: bool) {
66 self.mode = EffectReplayMode::Replay;
67 self.replay_effects = effects;
68 self.replay_pos = 0;
69 self.validate_replay_args = validate_args;
70 self.recorded_effects.clear();
71 }
72
73 pub fn take_recorded_effects(&mut self) -> Vec<EffectRecord> {
74 std::mem::take(&mut self.recorded_effects)
75 }
76
77 pub fn recorded_effects(&self) -> &[EffectRecord] {
78 &self.recorded_effects
79 }
80
81 pub fn replay_progress(&self) -> (usize, usize) {
82 (self.replay_pos, self.replay_effects.len())
83 }
84
85 pub fn ensure_replay_consumed(&self) -> Result<(), ReplayFailure> {
86 if self.mode == EffectReplayMode::Replay && self.replay_pos < self.replay_effects.len() {
87 return Err(ReplayFailure::Unconsumed {
88 remaining: self.replay_effects.len() - self.replay_pos,
89 });
90 }
91 Ok(())
92 }
93
94 pub fn record_effect(
95 &mut self,
96 effect_type: &str,
97 args: Vec<JsonValue>,
98 outcome: RecordedOutcome,
99 ) {
100 let seq = self.recorded_effects.len() as u32 + 1;
101 self.recorded_effects.push(EffectRecord {
102 seq,
103 effect_type: effect_type.to_string(),
104 args,
105 outcome,
106 });
107 }
108
109 pub fn replay_effect(
110 &mut self,
111 effect_type: &str,
112 got_args: Option<Vec<JsonValue>>,
113 ) -> Result<RecordedOutcome, ReplayFailure> {
114 if self.replay_pos >= self.replay_effects.len() {
115 return Err(ReplayFailure::Exhausted {
116 effect_type: effect_type.to_string(),
117 position: self.replay_pos + 1,
118 });
119 }
120
121 let record = self.replay_effects[self.replay_pos].clone();
122 if record.effect_type != effect_type {
123 return Err(ReplayFailure::Mismatch {
124 seq: record.seq,
125 expected: record.effect_type,
126 got: effect_type.to_string(),
127 });
128 }
129
130 if self.validate_replay_args
131 && let Some(got_args) = got_args
132 && got_args != record.args
133 {
134 return Err(ReplayFailure::ArgsMismatch {
135 seq: record.seq,
136 effect_type: effect_type.to_string(),
137 expected: json_to_string(&JsonValue::Array(record.args.clone())),
138 got: json_to_string(&JsonValue::Array(got_args)),
139 });
140 }
141
142 self.replay_pos += 1;
143 Ok(record.outcome)
144 }
145}