use std::sync::{Arc, Mutex};
use pamoja_core::{Actuator, Result};
#[derive(Clone, Debug)]
pub struct RecordingActuator<C> {
log: Arc<Mutex<Vec<C>>>,
}
impl<C> RecordingActuator<C> {
pub fn new() -> Self {
Self {
log: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn log(&self) -> ActuatorLog<C> {
ActuatorLog {
log: Arc::clone(&self.log),
}
}
}
impl<C> Default for RecordingActuator<C> {
fn default() -> Self {
Self::new()
}
}
impl<C> Actuator for RecordingActuator<C> {
type Command = C;
async fn apply(&mut self, command: C) -> Result<()> {
self.log.lock().expect("actuator log lock").push(command);
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ActuatorLog<C> {
log: Arc<Mutex<Vec<C>>>,
}
impl<C: Clone> ActuatorLog<C> {
pub fn commands(&self) -> Vec<C> {
self.log.lock().expect("actuator log lock").clone()
}
}
impl<C> ActuatorLog<C> {
pub fn len(&self) -> usize {
self.log.lock().expect("actuator log lock").len()
}
pub fn is_empty(&self) -> bool {
self.log.lock().expect("actuator log lock").is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn it_records_each_command_in_order() {
let mut relay = RecordingActuator::new();
let log = relay.log();
assert!(log.is_empty());
relay.apply(true).await.unwrap();
relay.apply(false).await.unwrap();
relay.apply(true).await.unwrap();
assert_eq!(log.commands(), vec![true, false, true]);
assert_eq!(log.len(), 3);
assert!(!log.is_empty());
}
#[tokio::test]
async fn a_log_taken_early_sees_later_commands() {
let relay = RecordingActuator::new();
let log = relay.log();
let mut moved = relay.clone();
moved.apply(42u8).await.unwrap();
assert_eq!(log.commands(), vec![42u8]);
}
}