use std::ops::Deref;
use std::str::FromStr;
use crate::error::{Error, Result};
pub trait Split {
fn name(&self) -> &str;
}
#[derive(Clone, Debug)]
pub struct ProportionSplit {
name: String,
pub proportion: f64,
}
impl Split for ProportionSplit {
fn name(&self) -> &str {
&self.name
}
}
impl FromStr for ProportionSplit {
type Err = Error;
fn from_str(spec: &str) -> Result<Self> {
let split: Vec<&str> = spec.split('=').collect();
if split.len() != 2 {
return Err(Error::InvalidSplitSpecification(spec.to_string()));
}
let proportion = split[1]
.parse::<f64>()
.map_err(|_| Error::InvalidSplitSpecification(spec.to_string()))?;
if proportion <= 0.0 {
return Err(Error::ProportionTooLow(spec.to_string()));
} else if proportion > 1.0 {
return Err(Error::ProportionTooHigh(spec.to_string()));
}
Ok(ProportionSplit {
name: split[0].to_string(),
proportion,
})
}
}
#[derive(Clone, Debug)]
pub struct RowSplit {
name: String,
pub total: f64,
pub done: f64,
}
impl Split for RowSplit {
fn name(&self) -> &str {
&self.name
}
}
impl FromStr for RowSplit {
type Err = Error;
fn from_str(spec: &str) -> Result<Self> {
let split: Vec<&str> = spec.split('=').collect();
if split.len() != 2 {
return Err(Error::InvalidSplitSpecification(spec.to_string()));
}
let total = split[1]
.parse::<u64>()
.map(|total| total as f64)
.map_err(|_| Error::InvalidSplitSpecification(spec.to_string()))?;
Ok(RowSplit {
name: split[0].to_string(),
total,
done: 0.0,
})
}
}
pub enum SplitEnum {
Rows(RowSplit),
Proportion(ProportionSplit),
}
impl Deref for SplitEnum {
type Target = dyn Split;
fn deref(&self) -> &Self::Target {
match self {
SplitEnum::Rows(r) => r,
SplitEnum::Proportion(p) => p,
}
}
}