widest_line/lib.rs
1#![deny(missing_docs)]
2
3//! A library for finding the widest line in a string.
4//!
5//! This crate provides functionality to measure the display width of lines
6//! in a string and return the width of the widest line.
7
8use string_width::string_width;
9
10/// Returns the width of the widest line in the given string.
11///
12/// This function splits the input string by newlines and measures the display
13/// width of each line using the `string-width` crate, which properly handles:
14/// - Unicode characters (including wide characters like CJK)
15/// - ANSI escape codes (colors, formatting)
16/// - Zero-width characters
17///
18/// # Arguments
19///
20/// * `string` - The input string to analyze
21///
22/// # Returns
23///
24/// The width (in columns) of the widest line in the string. Returns 0 for empty strings.
25///
26/// # Examples
27///
28/// ```
29/// use widest_line::widest_line;
30///
31/// // Basic usage
32/// let text = "Hello\nWorld!\nThis is a longer line";
33/// assert_eq!(widest_line(text), 21);
34///
35/// // Handles Unicode properly
36/// let unicode = "Hello\n世界"; // "世界" is 4 columns wide
37/// assert_eq!(widest_line(unicode), 5);
38///
39/// // Handles ANSI escape codes
40/// let colored = "Normal\n\x1b[31mRed text\x1b[0m";
41/// assert_eq!(widest_line(colored), 8); // ANSI codes don't count
42///
43/// // Empty string
44/// assert_eq!(widest_line(""), 0);
45/// ```
46pub fn widest_line(string: &str) -> usize {
47 string.lines().map(string_width).max().unwrap_or(0)
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53 use rstest::rstest;
54
55 #[rstest]
56 #[case("a", 1)]
57 #[case("a\nbe", 2)]
58 #[case("古\n\u{001B}[1m@\u{001B}[22m", 2)]
59 #[case("", 0)]
60 #[case("Hello\nWorld!\nThis is a longer line", 21)]
61 #[case("Single line", 11)]
62 #[case("Short\nMedium line\nThis is the longest line in the test", 36)]
63 // Additional edge cases
64 #[case("\n\n\n", 0)] // Only newlines
65 #[case("line1\n", 5)] // Trailing newline
66 #[case("\nline2", 5)] // Leading newline
67 #[case("tab\there", 7)] // Tab characters
68 #[case("emoji 🦀", 8)] // Emoji (2 columns wide)
69 #[case("世界", 4)] // CJK characters (2 columns each)
70 #[case("Hello\n世界\nWorld", 5)] // Mixed Unicode
71 fn test_widest_line(#[case] input: &str, #[case] expected: usize) {
72 assert_eq!(widest_line(input), expected);
73 }
74}