use crate::render::{Color, Theme};
use super::{PlotSeries, SeriesType};
#[derive(Clone, Debug, Default)]
pub struct SeriesManager {
pub(crate) series: Vec<PlotSeries>,
pub(crate) auto_color_index: usize,
}
impl SeriesManager {
pub fn new() -> Self {
Self {
series: Vec::new(),
auto_color_index: 0,
}
}
pub fn len(&self) -> usize {
self.series.len()
}
pub fn is_empty(&self) -> bool {
self.series.is_empty()
}
pub(crate) fn series(&self) -> &[PlotSeries] {
&self.series
}
pub fn auto_color_index(&self) -> usize {
self.auto_color_index
}
pub fn next_auto_color(&mut self, theme: &Theme) -> Color {
let color = theme.get_color(self.auto_color_index);
self.auto_color_index += 1;
color
}
pub(crate) fn push(&mut self, series: PlotSeries) {
self.series.push(series);
}
pub(crate) fn increment_auto_color(&mut self) {
self.auto_color_index += 1;
}
pub fn validate(&self) -> Result<(), &'static str> {
for series in &self.series {
match &series.series_type {
SeriesType::Line { x_data, y_data } | SeriesType::Scatter { x_data, y_data } => {
if x_data.len() != y_data.len() {
return Err("X and Y data must have the same length");
}
if x_data.is_empty() {
return Err("Data series cannot be empty");
}
}
SeriesType::Bar { categories, values } => {
if categories.len() != values.len() {
return Err("Categories and values must have the same length");
}
if categories.is_empty() {
return Err("Bar chart cannot have empty data");
}
}
SeriesType::ErrorBars {
x_data,
y_data,
y_errors,
} => {
if x_data.len() != y_data.len() || x_data.len() != y_errors.len() {
return Err("Error bar data must have matching lengths");
}
}
SeriesType::ErrorBarsXY {
x_data,
y_data,
x_errors,
y_errors,
} => {
if x_data.len() != y_data.len()
|| x_data.len() != x_errors.len()
|| x_data.len() != y_errors.len()
{
return Err("Error bar data must have matching lengths");
}
}
SeriesType::Histogram { data, .. } => {
if data.is_empty() {
return Err("Histogram data cannot be empty");
}
}
SeriesType::BoxPlot { data, .. } => {
if data.is_empty() {
return Err("Box plot data cannot be empty");
}
}
_ => {}
}
}
Ok(())
}
pub fn clear(&mut self) {
self.series.clear();
self.auto_color_index = 0;
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &PlotSeries> {
self.series.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_series_manager() {
let manager = SeriesManager::new();
assert!(manager.is_empty());
assert_eq!(manager.len(), 0);
assert_eq!(manager.auto_color_index(), 0);
}
#[test]
fn test_next_auto_color() {
let mut manager = SeriesManager::new();
let theme = Theme::default();
let color1 = manager.next_auto_color(&theme);
assert_eq!(manager.auto_color_index(), 1);
let color2 = manager.next_auto_color(&theme);
assert_eq!(manager.auto_color_index(), 2);
assert_ne!(color1, color2);
}
#[test]
fn test_clear() {
let mut manager = SeriesManager::new();
manager.auto_color_index = 5;
manager.clear();
assert!(manager.is_empty());
assert_eq!(manager.auto_color_index(), 0);
}
}