use rettle::{
Argument,
Pour,
};
use std::any::Any;
use serde::Serialize;
use std::fs::OpenOptions;
use std::path::Path;
pub struct PourCsvArg {
filepath: String,
}
impl PourCsvArg {
pub fn new(filepath: &str) -> PourCsvArg {
let filepath = String::from(filepath);
PourCsvArg { filepath }
}
}
impl Argument for PourCsvArg {
fn as_any(&self) -> &dyn Any {
self
}
}
pub struct PourCsTea {}
impl PourCsTea {
pub fn new<T: Send + Sync + Clone + Serialize + 'static>(name: &str, params: PourCsvArg) -> Box<Pour<T>> {
Box::new(Pour {
name: String::from(name),
computation: Box::new(|tea_batch, args| {
pour_to_csv::<T>(tea_batch, args)
}),
params: Some(Box::new(params))
})
}
}
fn pour_to_csv<T: Send + Sync + Clone + Serialize + 'static>(tea_batch: Vec<T>, args: &Option<Box<dyn Argument + Send>>) -> Vec<T> {
match args {
None => panic!("Need to pass \"filepath\" params!"),
Some(box_args) => {
let box_args = box_args.as_any().downcast_ref::<PourCsvArg>().unwrap();
let headers = !Path::new(&box_args.filepath).exists();
let file = OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(&box_args.filepath)
.unwrap();
let mut writer = csv::WriterBuilder::new().has_headers(headers).from_writer(file);
tea_batch.into_iter()
.map(|tea| {
writer.serialize(&tea).unwrap();
tea
})
.collect()
}
}
}
#[cfg(test)]
mod tests {
use super::{PourCsvArg, PourCsTea};
use rettle::Pot;
use serde::Serialize;
#[derive(Default, Clone, Debug, Serialize)]
struct TestCsTea {
id: i32,
name: String,
value: i32
}
#[test]
fn create_csv_args() {
let csv_args = PourCsvArg::new("fixtures/test.csv");
assert_eq!(csv_args.filepath, "fixtures/test.csv");
}
#[test]
fn create_pour_cstea() {
let csv_args = PourCsvArg::new("fixtures/test.csv");
let pour_cstea = PourCsTea::new::<TestCsTea>("test_csv", csv_args);
let new_pot = Pot::new()
.add_ingredient(pour_cstea);
assert_eq!(new_pot.get_recipe().read().unwrap().len(), 1);
assert_eq!(new_pot.get_recipe().read().unwrap()[0].get_name(), "test_csv");
}
}