use ratatui::style::Color;
#[cfg(test)]
use crate::ui::theme::Theme;
use crate::ui::theme::ThemeColors;
pub fn price_points(history: &[u64]) -> Vec<(f64, f64)> {
history
.iter()
.enumerate()
.map(|(i, &v)| (i as f64, v as f64 / 100.0))
.collect()
}
pub fn y_bounds(data: &[(f64, f64)]) -> [f64; 2] {
if data.is_empty() {
return [0.0, 1.0];
}
let min = data.iter().map(|p| p.1).fold(f64::INFINITY, f64::min);
let max = data.iter().map(|p| p.1).fold(f64::NEG_INFINITY, f64::max);
if (min - max).abs() < f64::EPSILON {
[min * 0.999, max * 1.001]
} else {
[min * 0.999, max * 1.001]
}
}
pub fn trend_color(data: &[(f64, f64)], colors: &ThemeColors) -> Color {
let first = data.first().map(|p| p.1).unwrap_or(0.0);
let last = data.last().map(|p| p.1).unwrap_or(0.0);
if last >= first {
colors.positive
} else {
colors.negative
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn price_points_empty() {
assert!(price_points(&[]).is_empty());
}
#[test]
fn price_points_converts_cents_to_dollars() {
let pts = price_points(&[15000, 15050, 14950]);
assert_eq!(pts.len(), 3);
assert!((pts[0].0 - 0.0).abs() < f64::EPSILON);
assert!((pts[0].1 - 150.00).abs() < 0.001);
assert!((pts[1].0 - 1.0).abs() < f64::EPSILON);
assert!((pts[1].1 - 150.50).abs() < 0.001);
assert!((pts[2].0 - 2.0).abs() < f64::EPSILON);
assert!((pts[2].1 - 149.50).abs() < 0.001);
}
#[test]
fn price_points_single_value() {
let pts = price_points(&[10000]);
assert_eq!(pts.len(), 1);
assert!((pts[0].0 - 0.0).abs() < f64::EPSILON);
assert!((pts[0].1 - 100.00).abs() < 0.001);
}
#[test]
fn y_bounds_empty_returns_default() {
let b = y_bounds(&[]);
assert!((b[0] - 0.0).abs() < f64::EPSILON);
assert!((b[1] - 1.0).abs() < f64::EPSILON);
}
#[test]
fn y_bounds_expands_by_padding() {
let data = vec![(0.0, 100.0), (1.0, 200.0)];
let b = y_bounds(&data);
assert!((b[0] - 100.0 * 0.999).abs() < 0.001, "min bound: {}", b[0]);
assert!((b[1] - 200.0 * 1.001).abs() < 0.001, "max bound: {}", b[1]);
}
#[test]
fn y_bounds_single_point() {
let data = vec![(0.0, 150.0)];
let b = y_bounds(&data);
assert!(b[0] < 150.0, "lower bound should be below value");
assert!(b[1] > 150.0, "upper bound should be above value");
}
#[test]
fn trend_color_up_is_positive() {
let c = Theme::Default.colors();
let data = vec![(0.0, 100.0), (1.0, 110.0)];
assert_eq!(trend_color(&data, &c), c.positive);
}
#[test]
fn trend_color_down_is_negative() {
let c = Theme::Default.colors();
let data = vec![(0.0, 110.0), (1.0, 100.0)];
assert_eq!(trend_color(&data, &c), c.negative);
}
#[test]
fn trend_color_flat_is_positive() {
let c = Theme::Default.colors();
let data = vec![(0.0, 100.0), (1.0, 100.0)];
assert_eq!(trend_color(&data, &c), c.positive);
}
#[test]
fn trend_color_empty_is_positive() {
let c = Theme::Default.colors();
assert_eq!(trend_color(&[], &c), c.positive);
}
#[test]
fn trend_color_uses_theme_colors() {
let c = Theme::Dark.colors();
let data = vec![(0.0, 100.0), (1.0, 110.0)];
assert_eq!(trend_color(&data, &c), c.positive);
}
}