cli_tool/
cli_tool.rs

1/// CLI tool example for measuring string widths
2///
3/// Run with: cargo run --example cli_tool -- "Hello 👋 World"
4/// Or: echo "Hello 👋 World" | cargo run --example cli_tool
5use 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        // Use command line argument
17        args[1..].join(" ")
18    } else {
19        // Read from stdin
20        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    // Basic width
40    let width = input.display_width();
41    println!("Display width: {} characters", width);
42
43    // Character count vs display width
44    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    // Detailed analysis
59    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    // Different width calculation options
81    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    // Terminal formatting example
111    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}