Skip to main content

flashmind_types/
humanize.rs

1//! Human-readable formatting for numbers, token counts, and pricing.
2
3use std::fmt;
4
5/// Format a token count as a compact human-readable string.
6///
7/// Uses SI-style suffixes: `1M`, `1.5M`, `128k`, `512`.
8pub fn format_tokens(tokens: u32) -> String {
9    if tokens >= 1_000_000 {
10        let whole = tokens / 1_000_000;
11        let rem = tokens % 1_000_000;
12        if rem == 0 {
13            format!("{whole}M")
14        } else {
15            format!("{:.1}M", tokens as f64 / 1_000_000.0)
16        }
17    } else if tokens >= 1_000 {
18        let whole = tokens / 1_000;
19        let rem = tokens % 1_000;
20        if rem == 0 {
21            format!("{whole}k")
22        } else {
23            format!("{:.1}k", tokens as f64 / 1_000.0)
24        }
25    } else {
26        format!("{tokens}")
27    }
28}
29
30/// Format a large number with comma separators: `1,234,567`.
31pub fn format_number(n: u64) -> String {
32    let s = n.to_string();
33    let mut result = String::with_capacity(s.len() + s.len() / 3);
34    for (i, ch) in s.chars().enumerate() {
35        if i > 0 && (s.len() - i).is_multiple_of(3) {
36            result.push(',');
37        }
38        result.push(ch);
39    }
40    result
41}
42
43/// Wrapper for displaying a token count in human-readable form via [`fmt::Display`].
44///
45/// ```
46/// use flashmind_types::humanize::Tokens;
47/// assert_eq!(Tokens(200_000).to_string(), "200k");
48/// assert_eq!(Tokens(1_000_000).to_string(), "1M");
49/// ```
50pub struct Tokens(pub u32);
51
52impl fmt::Display for Tokens {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        f.write_str(&format_tokens(self.0))
55    }
56}
57
58// ---------------------------------------------------------------------------
59// Tests
60// ---------------------------------------------------------------------------
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_format_tokens() {
68        assert_eq!(format_tokens(0), "0");
69        assert_eq!(format_tokens(512), "512");
70        assert_eq!(format_tokens(1_000), "1k");
71        assert_eq!(format_tokens(4_096), "4.1k");
72        assert_eq!(format_tokens(128_000), "128k");
73        assert_eq!(format_tokens(200_000), "200k");
74        assert_eq!(format_tokens(1_000_000), "1M");
75        assert_eq!(format_tokens(1_500_000), "1.5M");
76        assert_eq!(format_tokens(2_000_000), "2M");
77    }
78
79    #[test]
80    fn test_format_number() {
81        assert_eq!(format_number(0), "0");
82        assert_eq!(format_number(999), "999");
83        assert_eq!(format_number(1_000), "1,000");
84        assert_eq!(format_number(1_234_567), "1,234,567");
85    }
86
87    #[test]
88    fn test_tokens_display() {
89        assert_eq!(format!("{}", Tokens(128_000)), "128k");
90        assert_eq!(format!("{}", Tokens(1_000_000)), "1M");
91    }
92}