use ratatui::{
backend::TestBackend,
buffer::Buffer,
layout::Rect,
style::{Color, Style},
};
#[derive(Debug)]
pub struct BarChart {
data: Vec<(String, u64)>,
bar_width: u16,
bar_gap: u16,
bar_style: Style,
value_style: Style,
label_style: Style,
max_height: u16,
}
impl BarChart {
pub fn new(data: Vec<(String, u64)>) -> Self {
Self {
data,
bar_width: 3,
bar_gap: 1,
bar_style: Style::default(),
value_style: Style::default(),
label_style: Style::default(),
max_height: 10,
}
}
pub fn bar_style(mut self, style: Style) -> Self {
self.bar_style = style;
self
}
pub fn value_style(mut self, style: Style) -> Self {
self.value_style = style;
self
}
pub fn label_style(mut self, style: Style) -> Self {
self.label_style = style;
self
}
pub fn max_height(mut self, height: u16) -> Self {
self.max_height = height;
self
}
pub fn bar_width(mut self, width: u16) -> Self {
self.bar_width = width;
self
}
pub fn bar_gap(mut self, gap: u16) -> Self {
self.bar_gap = gap;
self
}
pub fn render(&self, _area: Rect, _buf: &mut Buffer) {
unimplemented!("BarChart::render() is not yet implemented")
}
pub fn from_array_with_colors(array: &[i32], highlights: &[usize]) -> Self {
let data: Vec<(String, u64)> = array
.iter()
.enumerate()
.map(|(i, &value)| (i.to_string(), value as u64))
.collect();
let mut chart = Self::new(data);
if highlights.len() == 2 {
chart = chart.bar_style(Style::default().fg(Color::Blue));
} else if highlights.len() == 1 {
chart = chart.bar_style(Style::default().fg(Color::Red));
}
chart
}
pub fn scale_for_terminal(mut self, terminal_width: u16, terminal_height: u16) -> Self {
let available_width = terminal_width.saturating_sub(4); let bar_count = self.data.len() as u16;
if bar_count > 0 {
let total_width_needed = bar_count * (self.bar_width + self.bar_gap);
if total_width_needed > available_width {
let new_bar_width = (available_width / bar_count).saturating_sub(1).max(1);
self.bar_width = new_bar_width;
self.bar_gap = if new_bar_width > 1 { 1 } else { 0 };
}
}
let available_height = terminal_height.saturating_sub(6); self.max_height = available_height.min(20);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use ratatui::{Terminal, style::Color};
#[test]
fn test_bar_chart_creation_from_array_data() {
let array_data = vec![5, 3, 8, 1, 9, 2];
let highlights = vec![];
let chart = BarChart::from_array_with_colors(&array_data, &highlights);
assert_eq!(chart.data.len(), 6);
assert_eq!(chart.data[0], ("0".to_string(), 5));
assert_eq!(chart.data[1], ("1".to_string(), 3));
assert_eq!(chart.data[4], ("4".to_string(), 9));
}
#[test]
fn test_color_mapping_for_operations() {
let array_data = vec![5, 3, 8, 1];
let comparison_highlights = vec![0, 2];
let comparison_chart =
BarChart::from_array_with_colors(&array_data, &comparison_highlights);
assert_eq!(comparison_chart.bar_style.fg, Some(Color::Blue));
let swap_highlights = vec![1];
let swap_chart = BarChart::from_array_with_colors(&array_data, &swap_highlights);
assert_eq!(swap_chart.bar_style.fg, Some(Color::Red));
let no_highlights = vec![];
let default_chart = BarChart::from_array_with_colors(&array_data, &no_highlights);
assert_eq!(default_chart.bar_style.fg, None);
}
#[test]
fn test_height_scaling_for_different_terminal_sizes() {
let array_data = vec![1, 2, 3, 4, 5];
let highlights = vec![];
let chart_small =
BarChart::from_array_with_colors(&array_data, &highlights).scale_for_terminal(40, 10);
assert!(chart_small.max_height <= 4);
let chart_large =
BarChart::from_array_with_colors(&array_data, &highlights).scale_for_terminal(120, 30);
assert!(chart_large.max_height >= 10); assert!(chart_large.max_height <= 20); }
#[test]
fn test_bar_width_scaling_for_many_elements() {
let array_data: Vec<i32> = (0..50).collect(); let highlights = vec![];
let chart =
BarChart::from_array_with_colors(&array_data, &highlights).scale_for_terminal(60, 20);
assert!(chart.bar_width <= 2);
let total_width = array_data.len() as u16 * (chart.bar_width + chart.bar_gap);
assert!(total_width <= 56); }
#[test]
#[should_panic(expected = "BarChart::render() is not yet implemented")]
fn test_rendering_to_ratatui_buffer_fails_as_expected() {
let array_data = vec![5, 3, 8, 1];
let highlights = vec![0, 2];
let chart = BarChart::from_array_with_colors(&array_data, &highlights);
let backend = TestBackend::new(40, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let area = f.area();
let mut buffer = Buffer::empty(area);
chart.render(area, &mut buffer);
})
.unwrap();
}
#[test]
fn test_builder_pattern_methods() {
let data = vec![("A".to_string(), 10), ("B".to_string(), 20)];
let chart = BarChart::new(data)
.bar_width(5)
.bar_gap(2)
.max_height(15)
.bar_style(Style::default().fg(Color::Green))
.value_style(Style::default().fg(Color::Yellow))
.label_style(Style::default().fg(Color::Cyan));
assert_eq!(chart.bar_width, 5);
assert_eq!(chart.bar_gap, 2);
assert_eq!(chart.max_height, 15);
assert_eq!(chart.bar_style.fg, Some(Color::Green));
assert_eq!(chart.value_style.fg, Some(Color::Yellow));
assert_eq!(chart.label_style.fg, Some(Color::Cyan));
}
#[test]
fn test_empty_data_handling() {
let empty_data = vec![];
let highlights = vec![];
let chart = BarChart::from_array_with_colors(&empty_data, &highlights);
assert_eq!(chart.data.len(), 0);
let scaled_chart = chart.scale_for_terminal(80, 20);
assert!(scaled_chart.bar_width >= 1); }
#[test]
fn test_single_element_array() {
let single_element = vec![42];
let highlights = vec![];
let chart = BarChart::from_array_with_colors(&single_element, &highlights);
assert_eq!(chart.data.len(), 1);
assert_eq!(chart.data[0], ("0".to_string(), 42));
let scaled_chart = chart.scale_for_terminal(80, 20);
assert!(scaled_chart.bar_width >= 1);
assert!(scaled_chart.max_height >= 10);
}
#[test]
fn test_large_values_handling() {
let large_values = vec![1000000, 2000000, 3000000];
let highlights = vec![];
let chart = BarChart::from_array_with_colors(&large_values, &highlights);
assert_eq!(chart.data.len(), 3);
assert_eq!(chart.data[0], ("0".to_string(), 1000000));
assert_eq!(chart.data[2], ("2".to_string(), 3000000));
}
#[test]
fn test_multiple_highlight_types() {
let array_data = vec![1, 2, 3, 4, 5];
let many_highlights = vec![0, 1, 2];
let chart = BarChart::from_array_with_colors(&array_data, &many_highlights);
assert_eq!(chart.bar_style.fg, None);
let no_highlights = vec![];
let chart_empty = BarChart::from_array_with_colors(&array_data, &no_highlights);
assert_eq!(chart_empty.bar_style.fg, None);
}
}