use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fs;
use serde::Serialize;
use countries::Country;
use keytree::{
KeyTree,
KeyTreeRef,
};
use time_series::{
RegularTimeSeries,
};
use crate::{ GraphicRange, SeriesId, SeriesSpec, Transform };
use crate::error::*;
#[derive(Debug)]
pub struct UIData(pub BTreeMap<(Country, usize), String>);
#[derive(Debug, Serialize)]
pub enum Scale {
Group {
x_min: f32,
x_max: f32,
y_min: f32,
y_max: f32,
},
Single {
x_min: f32,
x_max: f32,
y_min: f32,
y_max: f32,
},
}
pub struct Json(BTreeMap<(Country, usize), GraphicJson>);
impl Json {
pub fn into_data(&self) -> Result<UIData, Error> {
let mut map: BTreeMap<(Country, usize), String> = BTreeMap::new();
for (key, value) in self.0.iter() {
let json = serde_json::to_string(&value)
.map_err(|_| {
err(file!(), line!(), &format!("Serialize to JSON failed."))
})?;
map.insert(*key, json);
}
Ok(UIData(map))
}
}
#[derive(Debug, Serialize)]
pub struct GraphicJson {
country: Country,
index: usize,
lines: Vec<LineJson>,
scale: Scale,
x_range: Option<GraphicRange>,
y_range: Option<GraphicRange>,
}
#[derive(Debug, Serialize)]
pub struct LineJson {
x_series_id: SeriesId,
y_series_id: SeriesId,
rts: RegularTimeSeries<2>,
x_transforms: Vec<Transform>,
y_transforms: Vec<Transform>,
}
pub struct GraphicBuilder {
lines: Vec<LineJson>,
x_min: Option<f32>,
x_max: Option<f32>,
y_min: Option<f32>,
y_max: Option<f32>,
}
impl GraphicBuilder {
fn x_min(&mut self, x: f32) {
match self.x_min {
None => self.x_min = Some(x),
Some(old) => if x < old { self.x_min = Some(x) },
}
}
fn x_max(&mut self, x: f32) {
match self.x_max {
None => self.x_max = Some(x),
Some(old) => if x > old { self.x_max = Some(x) },
}
}
fn y_min(&mut self, y: f32) {
match self.y_min {
None => self.y_min = Some(y),
Some(old) => if y < old { self.y_min = Some(y) },
}
}
fn y_max(&mut self, y: f32) {
match self.y_max {
None => self.y_max = Some(y),
Some(old) => if y > old { self.y_max = Some(y) },
}
}
fn scale(&self) -> Scale {
Scale::Single {
x_min: self.x_min.unwrap(),
x_max: self.x_max.unwrap(),
y_min: self.y_min.unwrap(),
y_max: self.y_max.unwrap(),
}
}
}
#[derive(Debug)]
pub struct Spec(Vec<GraphicSpec>);
impl Spec {
pub fn from_file(path: &str) -> Result<Self, Error> {
let source_spec = match fs::read_to_string(path) {
Ok(ss) => ss,
Err(_) => { return Err(
err(file!(), line!(), &format!("Failed to read file {}", path))
)},
};
let kt = KeyTree::parse(&source_spec).unwrap();
let out = kt.to_ref().try_into().map_err(|e: keytree::error::Error| {
err(file!(), line!(), &e.to_string())
});
out
}
pub fn into_json(&self, root_path: &str) -> Result<Json, Error> {
let mut json = BTreeMap::new();
for GraphicSpec {
country,
index,
seriess,
lines,
x_range,
y_range,
} in &self.0 {
let mut graphic_json_builder = GraphicBuilder {
lines: Vec::new(),
x_min: None,
x_max: None,
y_min: None,
y_max: None,
};
for LineSpec {
x,
y,
} in lines {
let x_rts = match seriess.get(x) {
Some(s) => s.read_data_with_transforms(root_path)?,
None => {
return Err(err(file!(), line!(), &format! ("Series {} lookup failed.", y)))
},
};
let x_min = x_rts.min(0);
let x_max = x_rts.max(0);
let y_rts = match seriess.get(y) {
Some(series_json) => series_json.read_data_with_transforms(root_path)?,
None => {
return Err(err(file!(), line!(), &format! ("Series {} lookup failed.", y)))
},
};
let y_min = y_rts.min(0);
let y_max = y_rts.max(0);
let rts = x_rts.zip_one_one(y_rts)
.map_err(|e| err(file!(), line!(), &e.to_string()))?;
let line_json = LineJson {
x_series_id: x.clone(),
y_series_id: y.clone(),
rts: rts,
x_transforms: seriess.get(x).unwrap().transforms.clone(),
y_transforms: seriess.get(y).unwrap().transforms.clone(),
};
graphic_json_builder.lines.push(line_json);
graphic_json_builder.x_min(x_min);
graphic_json_builder.x_max(x_max);
graphic_json_builder.y_min(y_min);
graphic_json_builder.y_max(y_max);
}
let graphic_json = GraphicJson {
country: *country,
index: *index,
scale: graphic_json_builder.scale(),
lines: graphic_json_builder.lines,
x_range: *x_range,
y_range: *y_range,
};
json.insert((*country, *index), graphic_json);
}
Ok(Json(json))
}
}
impl<'a> TryInto<Spec> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<Spec, Self::Error> {
Ok(
Spec(self.vec_at("ui_spec::graphic")?)
)
}
}
#[derive(Debug)]
pub struct GraphicSpec {
pub country: Country,
pub index: usize,
pub seriess: BTreeMap<SeriesId, SeriesSpec>,
pub lines: Vec<LineSpec>,
pub x_range: Option<GraphicRange>,
pub y_range: Option<GraphicRange>,
}
impl<'a> TryInto<GraphicSpec> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<GraphicSpec, Self::Error> {
let v: Vec<SeriesSpec> = self.vec_at("graphic::series")?;
let mut seriess = BTreeMap::new();
for series_spec in v {
seriess.insert(series_spec.series_id.clone(), series_spec);
};
Ok(
GraphicSpec {
country: self.value("graphic::country")?,
index: self.value("graphic::index")?,
seriess,
lines: self.vec_at("graphic::line")?,
x_range: self.opt_value("graphic::x_range")?,
y_range: self.opt_value("graphic::y_range")?,
}
)
}
}
#[derive(Debug)]
pub struct LineSpec {
x: SeriesId,
y: SeriesId,
}
impl<'a> TryInto<LineSpec> for KeyTreeRef<'a> {
type Error = keytree::Error;
fn try_into(self) -> Result<LineSpec, Self::Error> {
Ok(
LineSpec {
x: self.value("line::x")?,
y: self.value("line::y")?,
}
)
}
}