use crate::eval::{Value, monadic_architecture::EffectInterpreter};
use crate::effects::{Effect, EffectfulComputation};
use crate::diagnostics::Result;
use super::{MockEffectResponse, MockEffectBehavior, EffectCall};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
#[derive(Debug, Default)]
pub struct MockEffectInterpreter {
responses: Arc<Mutex<HashMap<Effect, MockEffectResponse>>>,
call_log: Arc<Mutex<Vec<EffectCall>>>,
behavior: MockEffectBehavior,
}
impl MockEffectInterpreter {
pub fn new() -> Self {
Self {
responses: Arc::new(Mutex::new(HashMap::new())),
call_log: Arc::new(Mutex::new(Vec::new())),
behavior: MockEffectBehavior::default(),
}
}
pub fn add_response(&self, effect: Effect, response: MockEffectResponse) {
self.responses.lock().unwrap().insert(effect, response);
}
pub fn call_log(&self) -> Vec<EffectCall> {
self.call_log.lock().unwrap().clone()
}
pub fn clear_call_log(&self) {
self.call_log.lock().unwrap().clear();
}
}
#[async_trait]
impl EffectInterpreter for MockEffectInterpreter {
async fn interpret(&self, effect: EffectfulComputation) -> Result<Value> {
let effect_call = EffectCall {
effect: Effect::IO, args: vec![], timestamp: std::time::SystemTime::now(),
};
self.call_log.lock().unwrap().push(effect_call);
#[cfg(feature = "async-runtime")]
if self.behavior.processing_time_ms > 0 {
tokio::time::sleep(
tokio::time::Duration::from_millis(self.behavior.processing_time_ms)
).await;
}
#[cfg(not(feature = "async-runtime"))]
if self.behavior.processing_time_ms > 0 {
std::thread::sleep(std::time::Duration::from_millis(self.behavior.processing_time_ms));
}
Ok(Value::string("mock effect result".to_string()))
}
fn can_interpret(&self, effect: &Effect) -> bool {
!self.behavior.fail_on_unknown ||
self.responses.lock().unwrap().contains_key(effect)
}
fn available_effects(&self) -> Vec<Effect> {
self.responses.lock().unwrap().keys().cloned().collect()
}
}