use crate::ingredient::{Ingredient, Steep, Skim, Pour};
use std::sync::{mpsc, Arc, Mutex, RwLock};
use std::thread;
use std::time::Instant;
enum OrderTea {
NewOrder(Order),
Terminate
}
trait FnBox {
fn call_box(self: Box<Self>);
}
impl<F: FnOnce()> FnBox for F {
fn call_box(self: Box<F>) {
(*self)()
}
}
type Order = Box<dyn FnBox + Send + 'static>;
pub struct Brewery {
brewers: Vec<Brewer>,
sender: mpsc::Sender<OrderTea>,
start_time: Instant,
}
impl Brewery {
pub fn new(size: usize) -> Brewery {
assert!(size > 0);
let (sender, plain_rx) = mpsc::channel();
let rx = Arc::new(Mutex::new(plain_rx));
let mut brewers = Vec::with_capacity(size);
for id in 0 .. size {
brewers.push(Brewer::new(id, Arc::clone(&rx)));
}
Brewery {
brewers,
sender,
start_time: Instant::now(),
}
}
pub fn take_order<F>(&self, f: F)
where F: FnOnce() + Send + 'static
{
let order = Box::new(f);
self.sender
.send(OrderTea::NewOrder(order))
.unwrap();
}
pub fn get_brewer_info(&self) {
println!("Number of brewers: {}", &self.brewers.len());
}
}
impl Drop for Brewery {
fn drop(&mut self) {
println!("Sending terminate message to all brewers.");
for _ in &mut self.brewers {
self.sender.send(OrderTea::Terminate).unwrap();
}
for brewer in &mut self.brewers {
println!("\tLetting go brewer {}", brewer.id);
if let Some(thread) = brewer.thread.take() {
thread.join().unwrap();
}
}
println!("Elapsed time: {} ms", self.start_time.elapsed().as_millis());
}
}
struct Brewer {
id: usize,
thread: Option<thread::JoinHandle<()>>,
}
impl Brewer {
pub fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<OrderTea>>>) -> Brewer {
let thread = thread::spawn(move || {
loop {
let make_tea = receiver.lock()
.unwrap()
.recv()
.unwrap();
match make_tea {
OrderTea::NewOrder(order) => {
order.call_box();
},
OrderTea::Terminate => {
println!("Brewer {} was let go...", id);
break;
}
}
}
});
Brewer {
id,
thread: Some(thread),
}
}
}
pub fn make_tea<T: Send + 'static>(mut tea_batch: Vec<T>, recipe: Arc<RwLock<Vec<Box<dyn Ingredient<T> + Send + Sync>>>>) {
let recipe = recipe.read().unwrap();
for step in recipe.iter() {
if let Some(steep) = step.as_any().downcast_ref::<Steep<T>>() {
tea_batch = steep.exec(tea_batch);
} else if let Some(skim) = step.as_any().downcast_ref::<Skim<T>>() {
tea_batch = skim.exec(tea_batch);
} else if let Some(pour) = step.as_any().downcast_ref::<Pour<T>>() {
tea_batch = pour.exec(tea_batch);
}
}
}
#[cfg(test)]
mod tests {
use super::Brewery;
#[derive(Debug, PartialEq, Default)]
struct TestTea {
x: i32,
}
#[test]
fn create_brewery_with_brewers() {
let brewery = Brewery::new(4);
assert_eq!(brewery.brewers.len(), 4);
}
#[test]
#[should_panic]
fn create_brewery_with_no_brewers() {
let _brewery = Brewery::new(0);
}
}