Skip to main content

aver/replay/
runtime.rs

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}