vtcode_commons/
formatting.rs1pub fn format_size(size: u64) -> String {
5 const KB: u64 = 1024;
6 const MB: u64 = KB * 1024;
7 const GB: u64 = MB * 1024;
8
9 if size >= GB {
10 format!("{:.1}GB", size as f64 / GB as f64)
11 } else if size >= MB {
12 format!("{:.1}MB", size as f64 / MB as f64)
13 } else if size >= KB {
14 format!("{:.1}KB", size as f64 / KB as f64)
15 } else {
16 format!("{}B", size)
17 }
18}
19
20pub fn indent_block(text: &str, indent: &str) -> String {
22 if indent.is_empty() || text.is_empty() {
23 return text.to_string();
24 }
25 let mut indented = String::with_capacity(text.len() + indent.len() * text.lines().count());
26 for (idx, line) in text.split('\n').enumerate() {
27 if idx > 0 {
28 indented.push('\n');
29 }
30 if !line.is_empty() {
31 indented.push_str(indent);
32 }
33 indented.push_str(line);
34 }
35 indented
36}
37
38pub fn truncate_text(text: &str, max_len: usize, ellipsis: &str) -> String {
40 if text.chars().count() <= max_len {
41 return text.to_string();
42 }
43
44 let mut truncated = text.chars().take(max_len).collect::<String>();
45 truncated.push_str(ellipsis);
46 truncated
47}
48
49pub fn truncate_byte_budget(text: &str, max_bytes: usize, suffix: &str) -> String {
53 if text.len() <= max_bytes {
54 return text.to_string();
55 }
56 let mut end = max_bytes.min(text.len());
57 while end > 0 && !text.is_char_boundary(end) {
58 end -= 1;
59 }
60 format!("{}{suffix}", &text[..end])
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn truncate_byte_budget_ascii() {
69 assert_eq!(truncate_byte_budget("hello world", 5, "..."), "hello...");
70 assert_eq!(truncate_byte_budget("hi", 10, "..."), "hi");
71 }
72
73 #[test]
74 fn truncate_byte_budget_cjk_no_panic() {
75 let jp = "こんにちは";
77 assert_eq!(truncate_byte_budget(jp, 5, "…"), "こ…");
79 assert_eq!(truncate_byte_budget(jp, 6, "…"), "こん…");
81 }
82
83 #[test]
84 fn truncate_byte_budget_mixed_ascii_cjk() {
85 let mixed = "AB日本語CD";
86 assert_eq!(truncate_byte_budget(mixed, 4, ".."), "AB.."); assert_eq!(truncate_byte_budget(mixed, 5, ".."), "AB日.."); }
90
91 #[test]
92 fn truncate_byte_budget_emoji() {
93 let emoji = "👋🌍"; assert_eq!(truncate_byte_budget(emoji, 5, "!"), "👋!");
95 }
96
97 #[test]
98 fn truncate_byte_budget_zero() {
99 assert_eq!(truncate_byte_budget("abc", 0, "..."), "...");
100 }
101
102 #[test]
103 fn truncate_text_counts_chars_not_bytes() {
104 let jp = "あいうえお"; assert_eq!(truncate_text(jp, 3, "…"), "あいう…");
106 assert_eq!(truncate_text(jp, 5, "…"), "あいうえお");
107 }
108}