1use std::env;
6use std::io::{self, Read};
7use string_width::{
8 AmbiguousWidthTreatment, DisplayWidth, StringWidthOptions, string_width,
9 string_width_with_options,
10};
11
12fn main() {
13 let args: Vec<String> = env::args().collect();
14
15 let input = if args.len() > 1 {
16 args[1..].join(" ")
18 } else {
19 let mut buffer = String::new();
21 match io::stdin().read_to_string(&mut buffer) {
22 Ok(_) => buffer.trim_end().to_string(),
23 Err(e) => {
24 eprintln!("Error reading from stdin: {}", e);
25 std::process::exit(1);
26 }
27 }
28 };
29
30 if input.is_empty() {
31 eprintln!("Usage: {} <text> or echo <text> | {}", args[0], args[0]);
32 eprintln!(" Measures the display width of Unicode text");
33 std::process::exit(1);
34 }
35
36 println!("Text analysis for: '{}'", input);
37 println!("{}", "─".repeat(50));
38
39 let width = input.display_width();
41 println!("Display width: {} characters", width);
42
43 let char_count = input.chars().count();
45 let byte_count = input.len();
46 println!("Character count: {}", char_count);
47 println!("Byte count: {}", byte_count);
48
49 if width != char_count {
50 println!("Note: Display width differs from character count");
51 if width > char_count {
52 println!(" → Contains wide characters (CJK, emoji)");
53 } else {
54 println!(" → Contains zero-width or combining characters");
55 }
56 }
57
58 println!("\nDetailed breakdown:");
60 for (i, ch) in input.chars().enumerate() {
61 let ch_width = string_width(ch.to_string().as_str());
62 let code_point = ch as u32;
63
64 let char_type = match ch_width {
65 0 => "zero-width",
66 1 => "narrow",
67 2 => "wide",
68 _ => "unknown",
69 };
70
71 println!(
72 " {}: '{}' (U+{:04X}) - {} width",
73 i + 1,
74 ch,
75 code_point,
76 char_type
77 );
78 }
79
80 println!("\nWidth with different options:");
82
83 let narrow_ambiguous = StringWidthOptions {
84 count_ansi: false,
85 ambiguous_width: AmbiguousWidthTreatment::Narrow,
86 };
87 println!(
88 " Ambiguous as narrow: {}",
89 string_width_with_options(input.as_str(), narrow_ambiguous)
90 );
91
92 let wide_ambiguous = StringWidthOptions {
93 count_ansi: false,
94 ambiguous_width: AmbiguousWidthTreatment::Wide,
95 };
96 println!(
97 " Ambiguous as wide: {}",
98 string_width_with_options(input.as_str(), wide_ambiguous)
99 );
100
101 let count_ansi = StringWidthOptions {
102 count_ansi: true,
103 ambiguous_width: AmbiguousWidthTreatment::Narrow,
104 };
105 println!(
106 " Counting ANSI codes: {}",
107 string_width_with_options(input.as_str(), count_ansi)
108 );
109
110 println!("\nTerminal formatting examples:");
112 let box_width = 40;
113 let content_width = input.display_width();
114
115 if content_width <= box_width - 4 {
116 let padding = box_width - content_width - 4;
117 let left_pad = padding / 2;
118 let right_pad = padding - left_pad;
119
120 println!("┌{}┐", "─".repeat(box_width - 2));
121 println!(
122 "│{}{}{}│",
123 " ".repeat(left_pad + 1),
124 input,
125 " ".repeat(right_pad + 1)
126 );
127 println!("└{}┘", "─".repeat(box_width - 2));
128 } else {
129 println!(
130 "Text too wide for box (width: {}, box: {})",
131 content_width,
132 box_width - 4
133 );
134 }
135}