ggplot_rs/scale/
format.rs1pub fn label_comma(v: f64) -> String {
6 if v == v.round() && v.abs() < 1e15 {
7 let s = format!("{}", v as i64);
8 add_commas(&s)
9 } else {
10 let s = format!("{:.2}", v);
11 let s = s.trim_end_matches('0').trim_end_matches('.');
12 if let Some((int_part, dec_part)) = s.split_once('.') {
13 format!("{}.{}", add_commas(int_part), dec_part)
14 } else {
15 add_commas(s)
16 }
17 }
18}
19
20pub fn label_percent(v: f64) -> String {
22 let pct = v * 100.0;
23 if (pct - pct.round()).abs() < 1e-10 {
24 format!("{}%", pct.round() as i64)
25 } else {
26 format!("{:.1}%", pct)
27 }
28}
29
30pub fn label_dollar(v: f64) -> String {
32 if v < 0.0 {
33 format!("-${}", label_comma(-v))
34 } else {
35 format!("${}", label_comma(v))
36 }
37}
38
39pub fn label_scientific(v: f64) -> String {
41 if v == 0.0 {
42 return "0".to_string();
43 }
44 let exp = v.abs().log10().floor() as i32;
45 let mantissa = v / 10f64.powi(exp);
46 if (mantissa - mantissa.round()).abs() < 1e-10 {
47 format!("{}e{}", mantissa.round() as i64, exp)
48 } else {
49 let s = format!("{:.2}e{}", mantissa, exp);
50 if let Some((m, e)) = s.split_once('e') {
52 let m = m.trim_end_matches('0').trim_end_matches('.');
53 format!("{m}e{e}")
54 } else {
55 s
56 }
57 }
58}
59
60fn add_commas(s: &str) -> String {
61 let negative = s.starts_with('-');
62 let digits = if negative { &s[1..] } else { s };
63 let mut result = String::new();
64 for (i, ch) in digits.chars().rev().enumerate() {
65 if i > 0 && i % 3 == 0 {
66 result.push(',');
67 }
68 result.push(ch);
69 }
70 let formatted: String = result.chars().rev().collect();
71 if negative {
72 format!("-{formatted}")
73 } else {
74 formatted
75 }
76}
77
78pub type LabelFormatter = fn(f64) -> String;
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_label_comma() {
87 assert_eq!(label_comma(1000.0), "1,000");
88 assert_eq!(label_comma(1234567.0), "1,234,567");
89 assert_eq!(label_comma(42.0), "42");
90 assert_eq!(label_comma(-5000.0), "-5,000");
91 }
92
93 #[test]
94 fn test_label_percent() {
95 assert_eq!(label_percent(0.5), "50%");
96 assert_eq!(label_percent(0.0), "0%");
97 assert_eq!(label_percent(1.0), "100%");
98 assert_eq!(label_percent(0.123), "12.3%");
99 }
100
101 #[test]
102 fn test_label_dollar() {
103 assert_eq!(label_dollar(1000.0), "$1,000");
104 assert_eq!(label_dollar(0.0), "$0");
105 assert_eq!(label_dollar(-500.0), "-$500");
106 }
107
108 #[test]
109 fn test_label_scientific() {
110 assert_eq!(label_scientific(12345.0), "1.23e4");
111 assert_eq!(label_scientific(0.0), "0");
112 assert_eq!(label_scientific(100.0), "1e2");
113 }
114}