use rettle::{
Ingredient,
Argument,
Fill,
Brewery,
make_tea,
};
use std::sync::{Arc, RwLock};
use std::io::{BufReader};
use std::fs::File;
use std::any::Any;
use serde::Deserialize;
use std::fmt::Debug;
pub struct FillCsvArg {
filepath: String,
batch_size: usize,
}
impl FillCsvArg {
pub fn new(filepath: &str, batch_size: usize) -> FillCsvArg {
let filepath = String::from(filepath);
FillCsvArg { filepath, batch_size }
}
}
impl Argument for FillCsvArg {
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct FillCsTea {}
impl FillCsTea {
pub fn new<T: Send + Debug + ?Sized + 'static>(name: &str, source: &str, params: FillCsvArg) -> Box<Fill<T>>
where for<'de> T: Deserialize<'de>
{
Box::new(Fill {
name: String::from(name),
source: String::from(source),
computation: Box::new(|args, brewery, recipe| {
fill_from_csv::<T>(args, brewery, recipe);
}),
params: Some(Box::new(params))
})
}
}
fn call_brewery<T: Send + 'static>(brewery: &Brewery, recipe: Arc<RwLock<Vec<Box<dyn Ingredient<T> + Send + Sync>>>>, tea_batch: Vec<T>) {
brewery.take_order(|| {
make_tea(tea_batch, recipe);
});
}
fn fill_from_csv<T: Send + Debug + ?Sized + 'static>(args: &Option<Box<dyn Argument + Send>>, brewery: &Brewery, recipe: Arc<RwLock<Vec<Box<dyn Ingredient<T> + Send + Sync>>>>)
where for<'de> T: Deserialize<'de>
{
match args {
None => (),
Some(box_args) => {
let box_args = box_args.as_any().downcast_ref::<FillCsvArg>().unwrap();
let f = File::open(&box_args.filepath);
let mut rdr = match f {
Ok(f) => {
let reader = BufReader::new(f);
csv::Reader::from_reader(reader)
},
Err(e) => {
println!("Failed opening file! Error: {:?}", e);
return
},
};
let mut tea_batch: Vec<T> = Vec::with_capacity(box_args.batch_size);
for result in rdr.deserialize() {
if tea_batch.len() == box_args.batch_size {
let recipe = Arc::clone(&recipe);
call_brewery(brewery, recipe, tea_batch);
tea_batch = Vec::with_capacity(box_args.batch_size);
}
let tea: T = result.unwrap();
tea_batch.push(tea);
}
let recipe = Arc::clone(&recipe);
call_brewery(brewery, recipe, tea_batch);
}
}
}
#[cfg(test)]
mod tests {
use super::{FillCsvArg, FillCsTea};
use rettle::Pot;
use serde::Deserialize;
#[derive(Default, Clone, Debug, Deserialize)]
struct TestCsTea {
id: i32,
name: String,
value: i32
}
#[test]
fn create_csv_args() {
let csv_args = FillCsvArg::new("fixtures/test.csv", 50);
assert_eq!(csv_args.filepath, "fixtures/test.csv");
assert_eq!(csv_args.batch_size, 50);
}
#[test]
fn create_fill_cstea() {
let csv_args = FillCsvArg::new("fixtures/test.csv", 50);
let fill_cstea = FillCsTea::new::<TestCsTea>("test_csv", "fixture", csv_args);
let new_pot = Pot::new()
.add_source(fill_cstea);
assert_eq!(new_pot.get_sources().len(), 1);
assert_eq!(new_pot.get_sources()[0].get_name(), "test_csv");
}
}