1use crate::data::Value;
9
10pub use crate::locale::{CurrencyPosition, Locale};
11pub use crate::scale::format::{
12 label_bytes, label_comma, label_comma_locale, label_comma_marks, label_currency,
13 label_currency_locale, label_currency_marks, label_dollar, label_number, label_number_locale,
14 label_number_marks, label_ordinal, label_percent, label_percent_locale, label_percent_marks,
15 label_scientific, label_si, LabelFormatter,
16};
17
18pub fn format_timestamp(secs: f64, fmt: &str, locale: Option<&Locale>) -> String {
31 crate::scale::datetime::strftime_locale(secs, fmt, locale)
32}
33
34pub fn format_value(v: &Value) -> String {
49 match v {
50 Value::Str(s) => s.clone(),
51 Value::Bool(b) => b.to_string(),
52 Value::Na => String::new(),
53 Value::DateTime(secs) => crate::data::format_epoch_secs(*secs),
55 _ => v
56 .as_f64()
57 .map(|f| format!("{}", (f * 1000.0).round() / 1000.0))
58 .unwrap_or_default(),
59 }
60}
61
62pub fn format_value_locale(v: &Value, locale: &Locale) -> String {
73 match v {
74 Value::DateTime(secs) => locale.format_datetime(*secs),
75 Value::Str(_) | Value::Bool(_) | Value::Na => format_value(v),
76 _ => locale.localize_number(&format_value(v)),
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn formats_each_variant() {
86 assert_eq!(format_value(&Value::Str("a<b".into())), "a<b");
87 assert_eq!(format_value(&Value::Bool(true)), "true");
88 assert_eq!(format_value(&Value::Integer(-42)), "-42");
89 assert_eq!(format_value(&Value::Float(2.0)), "2");
90 assert_eq!(format_value(&Value::Float(0.12345)), "0.123");
91 assert_eq!(format_value(&Value::DateTime(3_661)), "1970-01-01 01:01:01");
92 assert_eq!(format_value(&Value::Na), "");
93 }
94
95 #[test]
96 fn formats_each_variant_with_locale() {
97 let de = Locale::de_de();
98 assert_eq!(format_value_locale(&Value::Str("1.5".into()), &de), "1.5");
99 assert_eq!(format_value_locale(&Value::Integer(-4200), &de), "-4.200");
100 assert_eq!(format_value_locale(&Value::Float(0.5), &de), "0,5");
101 assert_eq!(format_value_locale(&Value::Na, &de), "");
102 assert_eq!(
103 format_value_locale(&Value::DateTime(1_792_022_400), &Locale::en_us()),
104 "Oct 15, 2026"
105 );
106 assert_eq!(
107 format_value_locale(&Value::Float(1234.5), &Locale::en_us()),
108 "1,234.5"
109 );
110 }
111}
112
113#[cfg(test)]
114mod format_timestamp_tests {
115 use super::*;
116
117 #[test]
118 fn formats_like_the_datetime_scales() {
119 assert_eq!(
120 format_timestamp(0.0, "%Y-%m-%d %H:%M", None),
121 "1970-01-01 00:00"
122 );
123 assert_eq!(
124 format_timestamp(86_400.0 * 59.0, "%a %-d %b %y", None),
125 "Sun 1 Mar 70"
126 );
127 let de = Locale::from_tag("de-DE");
128 assert_eq!(
129 format_timestamp(86_400.0 * 59.0, "%A, %-d. %B %Y", Some(&de)),
130 "Sonntag, 1. März 1970"
131 );
132 }
133}