use std::env;
use std::io::{self, Read};
use string_width::{
AmbiguousWidthTreatment, DisplayWidth, StringWidthOptions, string_width,
string_width_with_options,
};
fn main() {
let args: Vec<String> = env::args().collect();
let input = if args.len() > 1 {
args[1..].join(" ")
} else {
let mut buffer = String::new();
match io::stdin().read_to_string(&mut buffer) {
Ok(_) => buffer.trim_end().to_string(),
Err(e) => {
eprintln!("Error reading from stdin: {}", e);
std::process::exit(1);
}
}
};
if input.is_empty() {
eprintln!("Usage: {} <text> or echo <text> | {}", args[0], args[0]);
eprintln!(" Measures the display width of Unicode text");
std::process::exit(1);
}
println!("Text analysis for: '{}'", input);
println!("{}", "─".repeat(50));
let width = input.display_width();
println!("Display width: {} characters", width);
let char_count = input.chars().count();
let byte_count = input.len();
println!("Character count: {}", char_count);
println!("Byte count: {}", byte_count);
if width != char_count {
println!("Note: Display width differs from character count");
if width > char_count {
println!(" → Contains wide characters (CJK, emoji)");
} else {
println!(" → Contains zero-width or combining characters");
}
}
println!("\nDetailed breakdown:");
for (i, ch) in input.chars().enumerate() {
let ch_width = string_width(ch.to_string().as_str());
let code_point = ch as u32;
let char_type = match ch_width {
0 => "zero-width",
1 => "narrow",
2 => "wide",
_ => "unknown",
};
println!(
" {}: '{}' (U+{:04X}) - {} width",
i + 1,
ch,
code_point,
char_type
);
}
println!("\nWidth with different options:");
let narrow_ambiguous = StringWidthOptions {
count_ansi: false,
ambiguous_width: AmbiguousWidthTreatment::Narrow,
};
println!(
" Ambiguous as narrow: {}",
string_width_with_options(input.as_str(), narrow_ambiguous)
);
let wide_ambiguous = StringWidthOptions {
count_ansi: false,
ambiguous_width: AmbiguousWidthTreatment::Wide,
};
println!(
" Ambiguous as wide: {}",
string_width_with_options(input.as_str(), wide_ambiguous)
);
let count_ansi = StringWidthOptions {
count_ansi: true,
ambiguous_width: AmbiguousWidthTreatment::Narrow,
};
println!(
" Counting ANSI codes: {}",
string_width_with_options(input.as_str(), count_ansi)
);
println!("\nTerminal formatting examples:");
let box_width = 40;
let content_width = input.display_width();
if content_width <= box_width - 4 {
let padding = box_width - content_width - 4;
let left_pad = padding / 2;
let right_pad = padding - left_pad;
println!("┌{}┐", "─".repeat(box_width - 2));
println!(
"│{}{}{}│",
" ".repeat(left_pad + 1),
input,
" ".repeat(right_pad + 1)
);
println!("└{}┘", "─".repeat(box_width - 2));
} else {
println!(
"Text too wide for box (width: {}, box: {})",
content_width,
box_width - 4
);
}
}