pub fn text_display_width(text: &str) -> usize {
text.chars()
.map(|c| {
match c as u32 {
0x00..=0x7F => 1,
0x80..=0x2FF => 1,
0x2190..=0x21FF => 2,
0x3000..=0x303F => 2,
0x3040..=0x309F => 2,
0x30A0..=0x30FF => 2,
0x4E00..=0x9FFF => 2,
0xAC00..=0xD7AF => 2,
0x3400..=0x4DBF => 2,
0xFF01..=0xFF60 => 2,
_ => 1,
}
})
.sum()
}
pub fn pad_text_to_width(
text: &str,
width: usize,
alignment: TextAlignment,
pad_char: char,
) -> String {
let text_width = text_display_width(text);
if text_width >= width {
return text.to_string();
}
let padding_needed = width - text_width;
match alignment {
TextAlignment::Left => {
format!("{}{}", text, pad_char.to_string().repeat(padding_needed))
}
TextAlignment::Right => {
format!("{}{}", pad_char.to_string().repeat(padding_needed), text)
}
TextAlignment::Center => {
let left_pad = padding_needed / 2;
let right_pad = padding_needed - left_pad;
format!(
"{}{}{}",
pad_char.to_string().repeat(left_pad),
text,
pad_char.to_string().repeat(right_pad)
)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TextAlignment {
Left,
#[allow(dead_code)]
Right,
#[allow(dead_code)]
Center,
}
pub fn get_terminal_width() -> usize {
if let Ok((width, _)) = crossterm::terminal::size() {
width as usize
} else {
80 }
}
pub fn format_token_for_display(token: &str) -> String {
const PREFIX_LEN: usize = 12;
const SUFFIX_LEN: usize = 8;
if token.len() <= PREFIX_LEN + SUFFIX_LEN {
if token.len() <= 6 {
let visible_chars = token.len().div_ceil(2);
format!("{}***", &token[..visible_chars])
} else {
let visible_chars = token.len() / 2;
format!("{}***", &token[..visible_chars])
}
} else {
format!(
"{}...{}",
&token[..PREFIX_LEN],
&token[token.len() - SUFFIX_LEN..]
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_text_display_width() {
assert_eq!(text_display_width("Hello"), 5);
assert_eq!(text_display_width("World"), 5);
assert_eq!(text_display_width("123"), 3);
assert_eq!(text_display_width("你好"), 4);
assert_eq!(text_display_width("测试"), 4);
assert_eq!(text_display_width("Hello你好"), 9); assert_eq!(text_display_width("测试123"), 7);
assert_eq!(text_display_width(""), 0);
assert_eq!(text_display_width("!@#$%"), 5);
assert_eq!(text_display_width("!()"), 6);
}
#[test]
fn test_pad_text_to_width() {
assert_eq!(
pad_text_to_width("Hello", 10, TextAlignment::Left, ' '),
"Hello "
);
assert_eq!(
pad_text_to_width("Hello", 10, TextAlignment::Right, ' '),
" Hello"
);
assert_eq!(
pad_text_to_width("Hi", 6, TextAlignment::Center, ' '),
" Hi "
);
assert_eq!(
pad_text_to_width("Hi", 7, TextAlignment::Center, ' '),
" Hi "
);
assert_eq!(
pad_text_to_width("Very long text", 5, TextAlignment::Left, ' '),
"Very long text"
);
assert_eq!(
pad_text_to_width("你好", 8, TextAlignment::Left, ' '),
"你好 "
);
assert_eq!(
pad_text_to_width("你好", 8, TextAlignment::Center, ' '),
" 你好 "
);
assert_eq!(
pad_text_to_width("test", 8, TextAlignment::Center, '-'),
"--test--"
);
}
#[test]
fn test_format_token_for_display() {
assert_eq!(format_token_for_display("abc"), "ab***");
assert_eq!(format_token_for_display("abcdef"), "abc***");
assert_eq!(format_token_for_display("abcdefgh"), "abcd***");
let long_token = "sk-ant-api03_abcdefghijklmnopqrstuvwxyz1234567890abcdefgh";
let formatted = format_token_for_display(long_token);
assert!(formatted.starts_with("sk-ant-api03"));
assert!(formatted.contains("..."));
assert!(formatted.ends_with("defgh"));
assert_eq!(formatted.len(), 12 + 3 + 8); }
}