use std::fs::remove_dir_all;
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use yaml_rust2::Yaml;
use crate::{EKOError, Operator, Result};
const EP_CMP_RTOL: f64 = 1e-5;
const EP_CMP_ATOL: f64 = 1e-3;
pub struct EvolutionPoint {
pub scale: f64,
pub nf: i64,
}
impl TryFrom<&Yaml> for EvolutionPoint {
type Error = EKOError;
fn try_from(yml: &Yaml) -> Result<Self> {
let scale = yml["scale"].as_f64();
let scale = match scale {
Some(scale) => scale,
None => yml["scale"].as_i64().ok_or(EKOError::KeyError(
"because failed to read scale as float from int".to_owned(),
))? as f64,
};
let nf = yml["nf"]
.as_i64()
.ok_or(EKOError::KeyError("because failed to read nf".to_owned()))?;
Ok(Self { scale, nf })
}
}
fn is_close(a: f64, b: f64, rtol: f64, atol: f64) -> bool {
(a - b).abs() <= atol + rtol * b.abs()
}
impl PartialEq for EvolutionPoint {
fn eq(&self, other: &Self) -> bool {
self.nf == other.nf && is_close(self.scale, other.scale, EP_CMP_RTOL, EP_CMP_ATOL)
}
}
impl Eq for EvolutionPoint {}
pub struct EKO {
path: PathBuf,
operators: crate::inventory::Inventory<EvolutionPoint>,
}
const DIR_OPERATORS: &str = "operators/";
const TAR_WRITER_CAPACITY: usize = 128 * 1024;
impl EKO {
fn assert_working_dir(&self) -> Result<()> {
self.path
.exists()
.then_some(())
.ok_or(EKOError::NoWorkingDir)
}
pub fn destroy(&self) -> Result<()> {
self.assert_working_dir()?;
Ok(remove_dir_all(&self.path)?)
}
pub fn write_and_destroy(&self, dst: PathBuf) -> Result<()> {
self.write(dst)?;
self.destroy()
}
pub fn write(&self, dst: PathBuf) -> Result<()> {
self.assert_working_dir()?;
let dst_file = File::create(dst)?;
let dst_file = BufWriter::with_capacity(TAR_WRITER_CAPACITY, dst_file);
let mut ar = tar::Builder::new(dst_file);
Ok(ar.append_dir_all(".", &self.path)?)
}
pub fn extract(src: PathBuf, dst: PathBuf) -> Result<Self> {
let mut ar = tar::Archive::new(File::open(src)?);
ar.unpack(&dst)?;
Self::load_opened(dst)
}
pub fn load_opened(path: PathBuf) -> Result<Self> {
let mut operators = crate::inventory::Inventory::new(path.join(DIR_OPERATORS));
operators.load_keys()?;
let obj = Self { path, operators };
obj.assert_working_dir()?;
Ok(obj)
}
pub fn available_operators(&self) -> Vec<&EvolutionPoint> {
self.operators.keys()
}
pub fn has_operator(&self, ep: &EvolutionPoint) -> bool {
self.operators.has(ep)
}
pub fn load_operator(&self, ep: &EvolutionPoint) -> Result<Operator> {
self.assert_working_dir()?;
self.operators.load(ep)
}
}