use std::{fmt::Write, marker::PhantomData, sync::Arc}; use crate::extract::{DataPoint, DataSet}; const DATE_DISPLAY_FORMAT: &[time::format_description::FormatItem] = time::macros::format_description!("[year]-[month]-[day]"); type SerializationChunk = Result; pub trait DataFormat { fn header(dataset: &DataSet) -> SerializationChunk; fn row(dataset: &DataSet, row: &DataPoint) -> SerializationChunk; const ROW_SEPARATOR: &'static str; const END: &'static str; } pub struct DataSerializer { dataset: Arc, index: Option, serializer: PhantomData, } impl DataSerializer { pub fn new(dataset: Arc, _: F) -> Self { Self { dataset, index: None, serializer: PhantomData, } } } impl Iterator for DataSerializer { type Item = SerializationChunk; fn next(&mut self) -> Option { match self.index { None => { self.index = Some(0); let header = F::header(&self.dataset); if self.dataset.rows.is_empty() { Some(header.map(|s| s + F::END)) } else { Some(header) } } Some(i) => { if let Some(row) = self.dataset.rows.get(i) { self.index = Some(i + 1); let serialized_row = F::row(&self.dataset, row); let suffix = if i == self.dataset.rows.len() - 1 { F::END } else { F::ROW_SEPARATOR }; Some(serialized_row.map(|s| s + suffix)) } else { None } } } } } pub struct Csv; impl DataFormat for Csv { fn header(dataset: &DataSet) -> SerializationChunk { let mut header = String::from("Date"); for column in dataset.columns.iter() { write!(&mut header, ",{}", column)?; } writeln!(&mut header)?; Ok(header) } fn row(dataset: &DataSet, datapoint: &DataPoint) -> SerializationChunk { let mut csv_row = datapoint .date .format(DATE_DISPLAY_FORMAT) .expect("Failed to format date!"); for column in dataset.columns.iter() { if let Some(val) = datapoint.values.get(column) { write!(&mut csv_row, ",{}", val)?; } else { write!(&mut csv_row, ",")?; } } writeln!(&mut csv_row)?; Ok(csv_row) } const ROW_SEPARATOR: &'static str = ""; const END: &'static str = ""; } pub struct Json; impl DataFormat for Json { fn header(dataset: &DataSet) -> SerializationChunk { let mut header = String::from(r#"{"columns":["Date""#); for column in dataset.columns.iter() { write!(&mut header, ",{}", json::stringify(column.as_str()))?; } write!(&mut header, r#"],"rows":["#)?; Ok(header) } fn row(dataset: &DataSet, datapoint: &DataPoint) -> SerializationChunk { let mut row = String::from(r#"{"Date":"#); write!( &mut row, r#""{}""#, datapoint .date .format(DATE_DISPLAY_FORMAT) .expect("Failed to format date!") )?; for column in dataset.columns.iter() { if let Some(val) = datapoint.values.get(column) { write!(&mut row, ",{}:{}", json::stringify(column.as_str()), val)?; } } row += "}"; Ok(row) } const ROW_SEPARATOR: &'static str = ","; const END: &'static str = "]}"; }