refactor serialization, add json
This commit is contained in:
parent
921b62ed97
commit
aa508a43cb
5 changed files with 155 additions and 69 deletions
127
src/serialize.rs
Normal file
127
src/serialize.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
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<String, std::fmt::Error>;
|
||||
|
||||
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<F: DataFormat> {
|
||||
dataset: Arc<DataSet>,
|
||||
index: Option<usize>,
|
||||
serializer: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: DataFormat> DataSerializer<F> {
|
||||
pub fn new(dataset: Arc<DataSet>, _: F) -> Self {
|
||||
Self {
|
||||
dataset,
|
||||
index: None,
|
||||
serializer: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: DataFormat> Iterator for DataSerializer<F> {
|
||||
type Item = SerializationChunk;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
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 = "]}";
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue