use std::{
sync::mpsc::{channel, Sender},
thread,
time::Duration,
};
pub type BoxResult<T> = Result<T, Box<dyn std::error::Error + Send>>;
pub trait Baby {
fn cry(&mut self, elapsed: usize) -> BoxResult<()>;
}
pub struct Cradle {
tx: Sender<Signal>,
jh: thread::JoinHandle<BoxResult<()>>,
}
impl Cradle {
pub fn new<B>(mut babies: Vec<B>) -> Self
where
B: Baby + Send + Sync + 'static,
{
let (tx, rx) = channel();
let jh = thread::spawn(move || {
if Signal::Start == rx.recv().unwrap() {
let mut elapsed = 0;
loop {
let signal = rx.try_recv();
match signal {
Ok(signal) => match signal {
Signal::Reset => elapsed = 0,
Signal::Stop => break,
_ => {}
},
_ => {
for baby in babies.iter_mut() {
baby.cry(elapsed)?;
}
thread::sleep(Duration::from_secs(1));
elapsed += 1;
}
}
}
}
Ok(())
});
Self { tx, jh }
}
pub fn start(&self) {
self.tx.send(Signal::Start).unwrap();
}
pub fn reset(&self) {
self.tx.send(Signal::Reset).unwrap();
}
pub fn stop(&self) {
self.tx.send(Signal::Stop).unwrap();
}
pub fn join(self) -> thread::Result<BoxResult<()>> {
self.jh.join()
}
}
#[derive(PartialEq)]
enum Signal {
Reset,
Start,
Stop,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cradle() {
struct BabyImpl {
times: usize,
}
impl Baby for BabyImpl {
fn cry(&mut self, elapsed: usize) -> BoxResult<()> {
if elapsed >= 2 && self.times > 0 {
self.times -= 1;
println!("Baby cries at {}", elapsed);
}
Ok(())
}
}
let cradle = Cradle::new(vec![BabyImpl { times: 2 }]);
cradle.start();
thread::sleep(Duration::from_secs(4));
cradle.stop();
cradle.join().unwrap().unwrap();
}
}