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;