Skip to main content

haystack_core/codecs/
shared.rs

1//! Shared formatting helpers used by multiple codecs.
2
3use chrono::{NaiveTime, Timelike};
4
5/// Format a float value compactly: no trailing ".0" for integers.
6///
7/// Handles INF, -INF, NaN as strings.
8pub fn format_number_val(val: f64) -> String {
9    if val.is_infinite() {
10        return if val > 0.0 {
11            "INF".to_string()
12        } else {
13            "-INF".to_string()
14        };
15    }
16    if val.is_nan() {
17        return "NaN".to_string();
18    }
19    let s = format!("{val}");
20    if s.ends_with(".0") && !s.contains('e') && !s.contains('E') {
21        s[..s.len() - 2].to_string()
22    } else {
23        s
24    }
25}
26
27/// Format a NaiveTime as `HH:MM:SS[.frac]`, trimming trailing fractional zeros.
28pub fn format_time(t: &NaiveTime) -> String {
29    let nanos = t.nanosecond();
30    if nanos == 0 {
31        t.format("%H:%M:%S").to_string()
32    } else {
33        let base = t.format("%H:%M:%S").to_string();
34        let frac = format!(".{:09}", nanos);
35        let trimmed = frac.trim_end_matches('0');
36        format!("{base}{trimmed}")
37    }
38}
39
40/// Format fractional seconds from nanoseconds, or empty string if zero.
41pub fn format_frac_seconds(nanos: u32) -> String {
42    if nanos > 0 {
43        let s = format!(".{:09}", nanos);
44        s.trim_end_matches('0').to_string()
45    } else {
46        String::new()
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn format_number_val_integer() {
56        assert_eq!(format_number_val(42.0), "42");
57    }
58
59    #[test]
60    fn format_number_val_zero() {
61        assert_eq!(format_number_val(0.0), "0");
62    }
63
64    #[test]
65    fn format_number_val_float() {
66        assert_eq!(format_number_val(72.5), "72.5");
67    }
68
69    #[test]
70    fn format_number_val_negative() {
71        assert_eq!(format_number_val(-23.45), "-23.45");
72    }
73
74    #[test]
75    fn format_number_val_inf() {
76        assert_eq!(format_number_val(f64::INFINITY), "INF");
77    }
78
79    #[test]
80    fn format_number_val_neg_inf() {
81        assert_eq!(format_number_val(f64::NEG_INFINITY), "-INF");
82    }
83
84    #[test]
85    fn format_number_val_nan() {
86        assert_eq!(format_number_val(f64::NAN), "NaN");
87    }
88
89    #[test]
90    fn format_time_no_frac() {
91        let t = NaiveTime::from_hms_opt(8, 12, 5).unwrap();
92        assert_eq!(format_time(&t), "08:12:05");
93    }
94
95    #[test]
96    fn format_time_with_millis() {
97        let t = NaiveTime::from_hms_milli_opt(14, 30, 0, 123).unwrap();
98        assert_eq!(format_time(&t), "14:30:00.123");
99    }
100
101    #[test]
102    fn format_frac_seconds_zero() {
103        assert_eq!(format_frac_seconds(0), "");
104    }
105
106    #[test]
107    fn format_frac_seconds_with_nanos() {
108        assert_eq!(format_frac_seconds(123000000), ".123");
109    }
110
111    #[test]
112    fn format_frac_seconds_fine_precision() {
113        assert_eq!(format_frac_seconds(123456789), ".123456789");
114    }
115}