#![warn(missing_docs)]
use crate::{utility, ChartEmptyCells, ChartRange, ColNum, Color, IntoChartRange, RowNum};
mod tests;
#[derive(Clone)]
pub struct Sparkline {
pub(crate) series_color: Color,
pub(crate) negative_points_color: Color,
pub(crate) axis_color: Color,
pub(crate) markers_color: Color,
pub(crate) first_point_color: Color,
pub(crate) last_point_color: Color,
pub(crate) high_point_color: Color,
pub(crate) low_point_color: Color,
pub(crate) data_range: ChartRange,
pub(crate) date_range: ChartRange,
pub(crate) ranges: Vec<(String, String)>,
pub(crate) sparkline_type: SparklineType,
pub(crate) show_high_point: bool,
pub(crate) show_low_point: bool,
pub(crate) show_first_point: bool,
pub(crate) show_last_point: bool,
pub(crate) show_negative_points: bool,
pub(crate) show_markers: bool,
pub(crate) show_axis: bool,
pub(crate) show_hidden_data: bool,
pub(crate) show_right_to_left: bool,
pub(crate) show_empty_cells_as: ChartEmptyCells,
pub(crate) line_weight: Option<f64>,
pub(crate) custom_min: Option<f64>,
pub(crate) custom_max: Option<f64>,
pub(crate) group_max: bool,
pub(crate) group_min: bool,
data_row_order: bool,
}
#[allow(clippy::new_without_default)]
impl Sparkline {
pub fn new() -> Sparkline {
Sparkline {
series_color: Color::Theme(4, 5),
negative_points_color: Color::Theme(5, 0),
axis_color: Color::Black,
markers_color: Color::Theme(4, 5),
first_point_color: Color::Theme(4, 3),
last_point_color: Color::Theme(4, 3),
high_point_color: Color::Theme(4, 0),
low_point_color: Color::Theme(4, 0),
data_range: ChartRange::default(),
date_range: ChartRange::default(),
ranges: vec![],
sparkline_type: SparklineType::Line,
show_high_point: false,
show_low_point: false,
show_first_point: false,
show_last_point: false,
show_negative_points: false,
show_markers: false,
show_axis: false,
show_hidden_data: false,
show_right_to_left: false,
show_empty_cells_as: ChartEmptyCells::Gaps,
line_weight: None,
custom_min: None,
custom_max: None,
group_max: false,
group_min: false,
data_row_order: true,
}
}
pub fn set_range<T>(mut self, range: T) -> Sparkline
where
T: IntoChartRange,
{
self.data_range = range.new_chart_range();
self
}
pub fn set_type(mut self, sparkline_type: SparklineType) -> Sparkline {
self.sparkline_type = sparkline_type;
self
}
pub fn show_high_point(mut self, enable: bool) -> Sparkline {
self.show_high_point = enable;
self
}
pub fn show_low_point(mut self, enable: bool) -> Sparkline {
self.show_low_point = enable;
self
}
pub fn show_first_point(mut self, enable: bool) -> Sparkline {
self.show_first_point = enable;
self
}
pub fn show_last_point(mut self, enable: bool) -> Sparkline {
self.show_last_point = enable;
self
}
pub fn show_negative_points(mut self, enable: bool) -> Sparkline {
self.show_negative_points = enable;
self
}
pub fn show_markers(mut self, enable: bool) -> Sparkline {
self.show_markers = enable;
self
}
pub fn show_axis(mut self, enable: bool) -> Sparkline {
self.show_axis = enable;
self
}
pub fn show_hidden_data(mut self, enable: bool) -> Sparkline {
self.show_hidden_data = enable;
self
}
pub fn show_empty_cells_as(mut self, option: ChartEmptyCells) -> Sparkline {
self.show_empty_cells_as = option;
self
}
pub fn set_right_to_left(mut self, enable: bool) -> Sparkline {
self.show_right_to_left = enable;
self
}
pub fn set_sparkline_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.series_color = color;
}
self
}
pub fn set_high_point_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.high_point_color = color;
self.show_high_point = true;
}
self
}
pub fn set_low_point_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.low_point_color = color;
self.show_low_point = true;
}
self
}
pub fn set_first_point_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.first_point_color = color;
self.show_first_point = true;
}
self
}
pub fn set_last_point_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.last_point_color = color;
self.show_last_point = true;
}
self
}
pub fn set_negative_points_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.negative_points_color = color;
self.show_negative_points = true;
}
self
}
pub fn set_markers_color(mut self, color: impl Into<Color>) -> Sparkline {
let color = color.into();
if color.is_valid() {
self.markers_color = color;
self.show_markers = true;
}
self
}
pub fn set_line_weight<T>(mut self, weight: T) -> Sparkline
where
T: Into<f64>,
{
self.line_weight = Some(weight.into());
self
}
pub fn set_custom_max<T>(mut self, max: T) -> Sparkline
where
T: Into<f64>,
{
self.custom_max = Some(max.into());
self.group_max = false;
self
}
pub fn set_custom_min<T>(mut self, min: T) -> Sparkline
where
T: Into<f64>,
{
self.custom_min = Some(min.into());
self.group_min = false;
self
}
pub fn set_group_max(mut self, enable: bool) -> Sparkline {
self.group_max = enable;
if enable {
self.custom_max = None;
}
self
}
pub fn set_group_min(mut self, enable: bool) -> Sparkline {
self.group_min = enable;
if enable {
self.custom_min = None;
}
self
}
pub fn set_date_range<T>(mut self, range: T) -> Sparkline
where
T: IntoChartRange,
{
self.date_range = range.new_chart_range();
self
}
pub fn set_column_order(mut self, enable: bool) -> Sparkline {
self.data_row_order = !enable;
self
}
#[allow(clippy::too_many_lines)]
#[allow(clippy::unreadable_literal)]
pub fn set_style(mut self, style: u8) -> Sparkline {
match style {
1 => {
self.low_point_color = Color::Theme(4, 0);
self.high_point_color = Color::Theme(4, 0);
self.last_point_color = Color::Theme(4, 3);
self.first_point_color = Color::Theme(4, 3);
self.markers_color = Color::Theme(4, 5);
self.negative_points_color = Color::Theme(5, 0);
self.series_color = Color::Theme(4, 5);
}
2 => {
self.low_point_color = Color::Theme(5, 0);
self.high_point_color = Color::Theme(5, 0);
self.last_point_color = Color::Theme(5, 3);
self.first_point_color = Color::Theme(5, 3);
self.markers_color = Color::Theme(5, 5);
self.negative_points_color = Color::Theme(6, 0);
self.series_color = Color::Theme(5, 5);
}
3 => {
self.low_point_color = Color::Theme(6, 0);
self.high_point_color = Color::Theme(6, 0);
self.last_point_color = Color::Theme(6, 3);
self.first_point_color = Color::Theme(6, 3);
self.markers_color = Color::Theme(6, 5);
self.negative_points_color = Color::Theme(7, 0);
self.series_color = Color::Theme(6, 5);
}
4 => {
self.low_point_color = Color::Theme(7, 0);
self.high_point_color = Color::Theme(7, 0);
self.last_point_color = Color::Theme(7, 3);
self.first_point_color = Color::Theme(7, 3);
self.markers_color = Color::Theme(7, 5);
self.negative_points_color = Color::Theme(8, 0);
self.series_color = Color::Theme(7, 5);
}
5 => {
self.low_point_color = Color::Theme(8, 0);
self.high_point_color = Color::Theme(8, 0);
self.last_point_color = Color::Theme(8, 3);
self.first_point_color = Color::Theme(8, 3);
self.markers_color = Color::Theme(8, 5);
self.negative_points_color = Color::Theme(9, 0);
self.series_color = Color::Theme(8, 5);
}
6 => {
self.low_point_color = Color::Theme(9, 0);
self.high_point_color = Color::Theme(9, 0);
self.last_point_color = Color::Theme(9, 3);
self.first_point_color = Color::Theme(9, 3);
self.markers_color = Color::Theme(9, 5);
self.negative_points_color = Color::Theme(4, 0);
self.series_color = Color::Theme(9, 5);
}
7 => {
self.low_point_color = Color::Theme(5, 4);
self.high_point_color = Color::Theme(5, 4);
self.last_point_color = Color::Theme(5, 4);
self.first_point_color = Color::Theme(5, 4);
self.markers_color = Color::Theme(5, 4);
self.negative_points_color = Color::Theme(5, 0);
self.series_color = Color::Theme(4, 4);
}
8 => {
self.low_point_color = Color::Theme(6, 4);
self.high_point_color = Color::Theme(6, 4);
self.last_point_color = Color::Theme(6, 4);
self.first_point_color = Color::Theme(6, 4);
self.markers_color = Color::Theme(6, 4);
self.negative_points_color = Color::Theme(6, 0);
self.series_color = Color::Theme(5, 4);
}
9 => {
self.low_point_color = Color::Theme(7, 4);
self.high_point_color = Color::Theme(7, 4);
self.last_point_color = Color::Theme(7, 4);
self.first_point_color = Color::Theme(7, 4);
self.markers_color = Color::Theme(7, 4);
self.negative_points_color = Color::Theme(7, 0);
self.series_color = Color::Theme(6, 4);
}
10 => {
self.low_point_color = Color::Theme(8, 4);
self.high_point_color = Color::Theme(8, 4);
self.last_point_color = Color::Theme(8, 4);
self.first_point_color = Color::Theme(8, 4);
self.markers_color = Color::Theme(8, 4);
self.negative_points_color = Color::Theme(8, 0);
self.series_color = Color::Theme(7, 4);
}
11 => {
self.low_point_color = Color::Theme(9, 4);
self.high_point_color = Color::Theme(9, 4);
self.last_point_color = Color::Theme(9, 4);
self.first_point_color = Color::Theme(9, 4);
self.markers_color = Color::Theme(9, 4);
self.negative_points_color = Color::Theme(9, 0);
self.series_color = Color::Theme(8, 4);
}
12 => {
self.low_point_color = Color::Theme(4, 4);
self.high_point_color = Color::Theme(4, 4);
self.last_point_color = Color::Theme(4, 4);
self.first_point_color = Color::Theme(4, 4);
self.markers_color = Color::Theme(4, 4);
self.negative_points_color = Color::Theme(4, 0);
self.series_color = Color::Theme(9, 4);
}
13 => {
self.low_point_color = Color::Theme(4, 4);
self.high_point_color = Color::Theme(4, 4);
self.last_point_color = Color::Theme(4, 4);
self.first_point_color = Color::Theme(4, 4);
self.markers_color = Color::Theme(4, 4);
self.negative_points_color = Color::Theme(5, 0);
self.series_color = Color::Theme(4, 0);
}
14 => {
self.low_point_color = Color::Theme(5, 4);
self.high_point_color = Color::Theme(5, 4);
self.last_point_color = Color::Theme(5, 4);
self.first_point_color = Color::Theme(5, 4);
self.markers_color = Color::Theme(5, 4);
self.negative_points_color = Color::Theme(6, 0);
self.series_color = Color::Theme(5, 0);
}
15 => {
self.low_point_color = Color::Theme(6, 4);
self.high_point_color = Color::Theme(6, 4);
self.last_point_color = Color::Theme(6, 4);
self.first_point_color = Color::Theme(6, 4);
self.markers_color = Color::Theme(6, 4);
self.negative_points_color = Color::Theme(7, 0);
self.series_color = Color::Theme(6, 0);
}
16 => {
self.low_point_color = Color::Theme(7, 4);
self.high_point_color = Color::Theme(7, 4);
self.last_point_color = Color::Theme(7, 4);
self.first_point_color = Color::Theme(7, 4);
self.markers_color = Color::Theme(7, 4);
self.negative_points_color = Color::Theme(8, 0);
self.series_color = Color::Theme(7, 0);
}
17 => {
self.low_point_color = Color::Theme(8, 4);
self.high_point_color = Color::Theme(8, 4);
self.last_point_color = Color::Theme(8, 4);
self.first_point_color = Color::Theme(8, 4);
self.markers_color = Color::Theme(8, 4);
self.negative_points_color = Color::Theme(9, 0);
self.series_color = Color::Theme(8, 0);
}
18 => {
self.low_point_color = Color::Theme(9, 4);
self.high_point_color = Color::Theme(9, 4);
self.last_point_color = Color::Theme(9, 4);
self.first_point_color = Color::Theme(9, 4);
self.markers_color = Color::Theme(9, 4);
self.negative_points_color = Color::Theme(4, 0);
self.series_color = Color::Theme(9, 0);
}
19 => {
self.low_point_color = Color::Theme(4, 5);
self.high_point_color = Color::Theme(4, 5);
self.last_point_color = Color::Theme(4, 4);
self.first_point_color = Color::Theme(4, 4);
self.markers_color = Color::Theme(4, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(4, 3);
}
20 => {
self.low_point_color = Color::Theme(5, 5);
self.high_point_color = Color::Theme(5, 5);
self.last_point_color = Color::Theme(5, 4);
self.first_point_color = Color::Theme(5, 4);
self.markers_color = Color::Theme(5, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(5, 3);
}
21 => {
self.low_point_color = Color::Theme(6, 5);
self.high_point_color = Color::Theme(6, 5);
self.last_point_color = Color::Theme(6, 4);
self.first_point_color = Color::Theme(6, 4);
self.markers_color = Color::Theme(6, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(6, 3);
}
22 => {
self.low_point_color = Color::Theme(7, 5);
self.high_point_color = Color::Theme(7, 5);
self.last_point_color = Color::Theme(7, 4);
self.first_point_color = Color::Theme(7, 4);
self.markers_color = Color::Theme(7, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(7, 3);
}
23 => {
self.low_point_color = Color::Theme(8, 5);
self.high_point_color = Color::Theme(8, 5);
self.last_point_color = Color::Theme(8, 4);
self.first_point_color = Color::Theme(8, 4);
self.markers_color = Color::Theme(8, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(8, 3);
}
24 => {
self.low_point_color = Color::Theme(9, 5);
self.high_point_color = Color::Theme(9, 5);
self.last_point_color = Color::Theme(9, 4);
self.first_point_color = Color::Theme(9, 4);
self.markers_color = Color::Theme(9, 1);
self.negative_points_color = Color::Theme(0, 5);
self.series_color = Color::Theme(9, 3);
}
25 => {
self.low_point_color = Color::Theme(1, 3);
self.high_point_color = Color::Theme(1, 3);
self.last_point_color = Color::Theme(1, 3);
self.first_point_color = Color::Theme(1, 3);
self.markers_color = Color::Theme(1, 3);
self.negative_points_color = Color::Theme(1, 3);
self.series_color = Color::Theme(1, 1);
}
26 => {
self.low_point_color = Color::Theme(0, 3);
self.high_point_color = Color::Theme(0, 3);
self.last_point_color = Color::Theme(0, 3);
self.first_point_color = Color::Theme(0, 3);
self.markers_color = Color::Theme(0, 3);
self.negative_points_color = Color::Theme(0, 3);
self.series_color = Color::Theme(1, 2);
}
27 => {
self.low_point_color = Color::RGB(0xD00000);
self.high_point_color = Color::RGB(0xD00000);
self.last_point_color = Color::RGB(0xD00000);
self.first_point_color = Color::RGB(0xD00000);
self.markers_color = Color::RGB(0xD00000);
self.negative_points_color = Color::RGB(0xD00000);
self.series_color = Color::RGB(0x323232);
}
28 => {
self.low_point_color = Color::RGB(0x00070C0);
self.high_point_color = Color::RGB(0x00070C0);
self.last_point_color = Color::RGB(0x00070C0);
self.first_point_color = Color::RGB(0x00070C0);
self.markers_color = Color::RGB(0x00070C0);
self.negative_points_color = Color::RGB(0x00070C0);
self.series_color = Color::RGB(0x000000);
}
29 => {
self.low_point_color = Color::RGB(0xD00000);
self.high_point_color = Color::RGB(0xD00000);
self.last_point_color = Color::RGB(0xD00000);
self.first_point_color = Color::RGB(0xD00000);
self.markers_color = Color::RGB(0xD00000);
self.negative_points_color = Color::RGB(0xD00000);
self.series_color = Color::RGB(0x376092);
}
30 => {
self.low_point_color = Color::RGB(0x000000);
self.high_point_color = Color::RGB(0x000000);
self.last_point_color = Color::RGB(0x000000);
self.first_point_color = Color::RGB(0x000000);
self.markers_color = Color::RGB(0x000000);
self.negative_points_color = Color::RGB(0x000000);
self.series_color = Color::RGB(0x00070C0);
}
31 => {
self.low_point_color = Color::RGB(0xFF5055);
self.high_point_color = Color::RGB(0x56BE79);
self.last_point_color = Color::RGB(0x359CEB);
self.first_point_color = Color::RGB(0x5687C2);
self.markers_color = Color::RGB(0xD70077);
self.negative_points_color = Color::RGB(0xFFB620);
self.series_color = Color::RGB(0x5F5F5F);
}
32 => {
self.low_point_color = Color::RGB(0xFF5055);
self.high_point_color = Color::RGB(0x56BE79);
self.last_point_color = Color::RGB(0x359CEB);
self.first_point_color = Color::RGB(0x777777);
self.markers_color = Color::RGB(0xD70077);
self.negative_points_color = Color::RGB(0xFFB620);
self.series_color = Color::RGB(0x5687C2);
}
33 => {
self.low_point_color = Color::RGB(0xFF5367);
self.high_point_color = Color::RGB(0x60D276);
self.last_point_color = Color::RGB(0xFFEB9C);
self.first_point_color = Color::RGB(0xFFDC47);
self.markers_color = Color::RGB(0x8CADD6);
self.negative_points_color = Color::RGB(0xFFC7CE);
self.series_color = Color::RGB(0xC6EFCE);
}
34 => {
self.low_point_color = Color::RGB(0xFF0000);
self.high_point_color = Color::RGB(0x00B050);
self.last_point_color = Color::RGB(0xFFC000);
self.first_point_color = Color::RGB(0xFFC000);
self.markers_color = Color::RGB(0x0070C0);
self.negative_points_color = Color::RGB(0xFF0000);
self.series_color = Color::RGB(0x0B050);
}
35 => {
self.low_point_color = Color::Theme(7, 0);
self.high_point_color = Color::Theme(6, 0);
self.last_point_color = Color::Theme(5, 0);
self.first_point_color = Color::Theme(4, 0);
self.markers_color = Color::Theme(8, 0);
self.negative_points_color = Color::Theme(9, 0);
self.series_color = Color::Theme(3, 0);
}
36 => {
self.low_point_color = Color::Theme(7, 0);
self.high_point_color = Color::Theme(6, 0);
self.last_point_color = Color::Theme(5, 0);
self.first_point_color = Color::Theme(4, 0);
self.markers_color = Color::Theme(8, 0);
self.negative_points_color = Color::Theme(9, 0);
self.series_color = Color::Theme(1, 0);
}
_ => eprintln!("Sparkline style '{style}' outside the Excel range 1-36."),
}
self
}
pub(crate) fn add_cell_range(&mut self, row: RowNum, col: ColNum) {
let cell = utility::row_col_to_cell(row, col);
let range = self.data_range.formula();
self.ranges.push((cell, range));
}
pub(crate) fn add_group_range(
&mut self,
first_row: RowNum,
first_col: ColNum,
last_row: RowNum,
last_col: ColNum,
) {
let cell_row_order = last_col - first_col == 0;
self.data_range.set_baseline(self.data_row_order);
if cell_row_order {
for row in first_row..=last_row {
let cell = utility::row_col_to_cell(row, first_col);
let range = self.data_range.formula();
self.ranges.push((cell, range));
self.data_range.increment(self.data_row_order);
}
} else {
for col in first_col..=last_col {
let cell = utility::row_col_to_cell(first_row, col);
let range = self.data_range.formula();
self.ranges.push((cell, range));
self.data_range.increment(self.data_row_order);
}
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum SparklineType {
Line,
Column,
WinLose,
}