convert_to_spaces/lib.rs
1/// Converts tabs in a string to a specified number of spaces, respecting tab stop logic.
2///
3/// Each tab character (`\t`) will advance the text to the next multiple of the
4/// `tab_size`. The number of spaces inserted will vary depending on the
5/// current column position.
6///
7/// # Arguments
8///
9/// * `input` - The string-like reference (`AsRef<str>`) to convert. This allows
10/// passing `&str`, `String`, `&String`, etc.
11/// * `tab_size` - The size of a tab stop. If 0, tabs are effectively removed.
12///
13/// # Returns
14///
15/// A `String` with all tab characters (`\t`) correctly replaced by spaces
16/// according to tab stop rules.
17///
18/// # Examples
19///
20/// ```
21/// use convert_to_spaces::convert_to_spaces;
22///
23/// assert_eq!(convert_to_spaces("SPACE\tTAB", 4), "SPACE TAB");
24/// assert_eq!(convert_to_spaces("\tHello", 4), " Hello");
25/// assert_eq!(convert_to_spaces("Line 1\n\tLine 2", 2), "Line 1\n Line 2");
26/// assert_eq!(convert_to_spaces(String::from("MyString\t"), 8), "MyString ");
27/// assert_eq!(convert_to_spaces("No tabs here", 4), "No tabs here");
28/// ```
29pub fn convert_to_spaces<S: AsRef<str>>(input: S, tab_size: usize) -> String {
30 let input_str = input.as_ref();
31
32 // If tab_size is 0, a tab will insert 0 spaces, effectively removing tabs.
33 if tab_size == 0 {
34 return input_str.replace('\t', "");
35 }
36
37 let mut result = String::new();
38 let mut current_column = 0;
39
40 for char_code in input_str.chars() {
41 match char_code {
42 '\t' => {
43 // Calculate how many spaces are needed to reach the next tab stop
44 let spaces_to_add = tab_size - (current_column % tab_size);
45 for _ in 0..spaces_to_add {
46 result.push(' ');
47 }
48 current_column += spaces_to_add;
49 }
50 '\n' => {
51 // Reset column count for a new line
52 result.push('\n');
53 current_column = 0;
54 }
55 c => {
56 // For other characters, simply append and increment column count
57 result.push(c);
58 // NOTE: This assumes character width is 1. For actual wide/emoji characters,
59 // `current_column` would need to be based on grapheme clusters
60 // and their display widths (e.g., using `unicode-width` crate).
61 current_column += 1;
62 }
63 }
64 }
65
66 result
67}
68
69#[cfg(test)]
70#[path = "lib_tests.rs"]
71mod tests;