use std::cmp::Ordering;
use std::collections::HashMap;
#[doc(inline)]
use crate::chart::{Percentages, TagPercent};
#[derive(Debug)]
pub struct PieData {
total: f32,
data: HashMap<String, f32>
}
impl PieData {
pub fn add_secs(&mut self, label: &str, secs: u64) {
#![allow(clippy::cast_precision_loss)]
let secs = secs as f32;
self.total += secs;
let entry = self.data.entry(label.to_string()).or_insert(0.0);
*entry += secs;
}
pub fn get_percent(&self, label: &str) -> Option<f32> {
self.data.get(label).map(|&s| self.cent(s))
}
pub fn is_empty(&self) -> bool { self.data.is_empty() }
pub fn total(&self) -> f32 { self.total }
pub fn labels(&self) -> Vec<&String> {
let mut labels: Vec<&String> = self.data.keys().collect();
labels.sort();
labels
}
pub fn percentages(&self) -> Percentages {
let mut percents: Percentages = self
.data
.iter()
.filter_map(|(l, p)| TagPercent::new(l, self.cent(*p)))
.collect();
percents.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
percents
}
fn cent(&self, val: f32) -> f32 { 100.0 * val / self.total }
}
impl Default for PieData {
fn default() -> Self { Self { total: 0.0, data: HashMap::new() } }
}
#[cfg(test)]
mod tests {
use spectral::prelude::*;
use super::*;
#[test]
fn test_default() {
let pd = PieData::default();
assert_that!(pd.is_empty()).is_true();
assert_that!(pd.total()).is_equal_to(&0.0);
assert_that!(pd.labels().len()).is_equal_to(&0);
assert_that!(pd.percentages().len()).is_equal_to(&0);
}
#[test]
fn test_add_secs() {
let mut pd = PieData::default();
pd.add_secs("foo", 300);
assert_that!(pd.is_empty()).is_false();
assert_that!(pd.total()).is_equal_to(&300.0);
assert_that!(pd.labels().len()).is_equal_to(&1);
assert_that!(pd.labels()).is_equal_to(&vec![&("foo".to_string())]);
assert_that!(pd.percentages()).is_equal_to(&vec![TagPercent::new("foo", 100.0).unwrap()]);
}
#[test]
fn test_add_secs_multiple() {
let mut pd = PieData::default();
#[rustfmt::skip]
let input = [
("david", 400),
("kirsten", 300),
("mark", 200),
("connie", 100)
];
for (label, secs) in &input {
pd.add_secs(label, *secs);
}
assert_that!(pd.total()).is_equal_to(&1000.0);
let expect: Vec<String> = ["connie", "david", "kirsten", "mark"]
.iter()
.map(|s| s.to_string())
.collect();
assert_that!(pd.labels()).is_equal_to(&expect.iter().collect::<Vec<&String>>());
let expect: Vec<TagPercent> = input
.iter()
.map(|(l, s)| TagPercent::new(l, *s as f32 / 10.0).unwrap())
.collect();
assert_that!(pd.percentages()).is_equal_to(&expect);
}
}