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}