1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::f64;
pub fn parse_numbers (args : &Vec<String>) -> Vec<f64> {
let parsed_numbers = args.iter().map(|ref x| x.parse::<f64>()).collect::<Vec<_>>();
let mut good_numbers = Vec::new();
for (i, result) in parsed_numbers.iter().enumerate() {
let num : f64 = result.clone().ok().expect(&*format!("Argument \"{}\" was not a number :(", args[i]));
good_numbers.push(num);
}
good_numbers
}
pub fn min_max_for_data (numbers: &Vec<f64>, min_opt: Option<f64>, max_opt: Option<f64>) -> (f64, f64) {
let max = match max_opt {
Some(m) => m,
None => numbers.iter().fold(f64::NEG_INFINITY, |a, b| a.max(*b)),
};
let min = match min_opt {
Some(m) => m,
None => numbers.iter().fold(f64::INFINITY, |a, b| a.min(*b)),
};
(min, max)
}
pub enum SparkThemeName {
Classic,
Colour,
}
pub struct SparkTheme {
pub sparks : Vec<String>,
}
impl SparkTheme {
pub fn spark(&self, min : f64, max : f64, num : f64) -> &String {
let increments = self.sparks.len() as f64;
let mut proportion = (increments) * ((num - min) / (max - min));
if proportion == increments { proportion = proportion - 1.0; }
&self.sparks[proportion as usize]
}
}
fn colorise(x : &String) -> String {
let reset = "\x1B[0m";
match &**x {
"▁"|"▂" => "\x1B[0;32m".to_string() + x + reset,
"▃"|"▄" => "\x1B[0;33m".to_string() + x + reset,
"▅"|"▆" => "\x1B[0;33m".to_string() + x + reset,
"▇"|"█" => "\x1B[0;31m".to_string() + x + reset,
_ => x.clone(),
}
}
pub fn select_sparkline(st : SparkThemeName) -> SparkTheme {
let sparks = "▁▂▃▄▅▆▇█";
match st {
SparkThemeName::Classic => {
SparkTheme {
sparks: sparks.chars().map(|x| x.to_string()).collect()
}
},
SparkThemeName::Colour => {
let spark_chars : Vec<String> = sparks.chars().map(|x| colorise(&x.to_string())).collect();
SparkTheme {
sparks: spark_chars
}
},
}
}
#[test]
fn test_sparkline_mapping() {
use SparkTheme;
let (min, max) : (f64, f64) = (0.0, 10.0);
let values = vec![2.0, 3.0, 2.0, 6.0, 9.0];
let expected = "▂▃▂▅█".to_string();
let sparky = select_sparkline(SparkThemeName::Classic);
for (num, compare) in values.iter().zip(expected.chars()) {
let s : &String = sparky.spark(min, max, *num);
println!("{}", num);
assert_eq!(*s, compare.to_string());
}
}