use std::sync::{Arc, Mutex, RwLock};
use packr::abi::Value;
use packr::CallInterceptor;
use crate::chain::StateChain;
use crate::events::{ChainEventData, ChainEventPayload};
use crate::replay::HostFunctionCall;
pub struct RecordingInterceptor {
chain: Arc<RwLock<StateChain>>,
}
impl RecordingInterceptor {
pub fn new(chain: Arc<RwLock<StateChain>>) -> Self {
Self { chain }
}
}
impl CallInterceptor for RecordingInterceptor {
fn before_import(&self, _interface: &str, _function: &str, _input: &Value) -> Option<Value> {
None }
fn after_import(&self, interface: &str, function: &str, input: &Value, output: &Value) {
let call = HostFunctionCall::new(interface, function, input.clone(), output.clone());
let mut chain = self.chain.write().unwrap();
let _ = chain.add_typed_event(ChainEventData {
event_type: format!("{}/{}", interface, function),
data: ChainEventPayload::HostFunction(call),
});
}
fn before_export(&self, _function: &str, _input: &Value) -> Option<Value> {
None }
fn after_export(&self, _function: &str, _input: &Value, _output: &Value) {
}
}
pub struct ReplayRecordingInterceptor {
expected_events: Vec<crate::chain::ChainEvent>,
position: Mutex<usize>,
chain: Arc<RwLock<StateChain>>,
}
impl ReplayRecordingInterceptor {
pub fn new(
expected_events: Vec<crate::chain::ChainEvent>,
chain: Arc<RwLock<StateChain>>,
) -> Self {
Self {
expected_events,
position: Mutex::new(0),
chain,
}
}
fn find_next_host_call(&self, interface: &str, function: &str) -> Option<HostFunctionCall> {
let mut pos = self.position.lock().unwrap();
while *pos < self.expected_events.len() {
let event = &self.expected_events[*pos];
if let Some(call) = crate::events::decode_host_function_call(&event.data) {
if call.interface == interface && call.function == function {
*pos += 1;
return Some(call);
}
}
*pos += 1;
}
None
}
}
impl CallInterceptor for ReplayRecordingInterceptor {
fn before_import(&self, interface: &str, function: &str, _input: &Value) -> Option<Value> {
self.find_next_host_call(interface, function)
.map(|call| call.output)
}
fn after_import(&self, interface: &str, function: &str, input: &Value, output: &Value) {
let call = HostFunctionCall::new(interface, function, input.clone(), output.clone());
let mut chain = self.chain.write().unwrap();
let _ = chain.add_typed_event(ChainEventData {
event_type: format!("{}/{}", interface, function),
data: ChainEventPayload::HostFunction(call),
});
}
fn before_export(&self, _function: &str, _input: &Value) -> Option<Value> {
None }
fn after_export(&self, _function: &str, _input: &Value, _output: &Value) {
}
}