- Add new documentation sections for PostgreSQL installer functions and usage examples. Improves clarity and completeness of the documentation. - Add new files and patterns to .gitignore to prevent unnecessary files from being committed to the repository. Improves repository cleanliness and reduces clutter.
138 lines
3.5 KiB
Rust
138 lines
3.5 KiB
Rust
/**
|
|
* Dedent a multiline string by removing common leading whitespace.
|
|
*
|
|
* This function analyzes all non-empty lines in the input text to determine
|
|
* the minimum indentation level, then removes that amount of whitespace
|
|
* from the beginning of each line. This is useful for working with
|
|
* multi-line strings in code that have been indented to match the
|
|
* surrounding code structure.
|
|
*
|
|
* # Arguments
|
|
*
|
|
* * `text` - The multiline string to dedent
|
|
*
|
|
* # Returns
|
|
*
|
|
* * `String` - The dedented string
|
|
*
|
|
* # Examples
|
|
*
|
|
* ```
|
|
* use sal::text::dedent;
|
|
*
|
|
* let indented = " line 1\n line 2\n line 3";
|
|
* let dedented = dedent(indented);
|
|
* assert_eq!(dedented, "line 1\nline 2\n line 3");
|
|
* ```
|
|
*
|
|
* # Notes
|
|
*
|
|
* - Empty lines are preserved but have all leading whitespace removed
|
|
* - Tabs are counted as 4 spaces for indentation purposes
|
|
*/
|
|
pub fn dedent(text: &str) -> String {
|
|
let lines: Vec<&str> = text.lines().collect();
|
|
|
|
// Find the minimum indentation level (ignore empty lines)
|
|
let min_indent = lines
|
|
.iter()
|
|
.filter(|line| !line.trim().is_empty())
|
|
.map(|line| {
|
|
let mut spaces = 0;
|
|
for c in line.chars() {
|
|
if c == ' ' {
|
|
spaces += 1;
|
|
} else if c == '\t' {
|
|
spaces += 4; // Count tabs as 4 spaces
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
spaces
|
|
})
|
|
.min()
|
|
.unwrap_or(0);
|
|
|
|
// Remove that many spaces from the beginning of each line
|
|
lines
|
|
.iter()
|
|
.map(|line| {
|
|
if line.trim().is_empty() {
|
|
return String::new();
|
|
}
|
|
|
|
let mut count = 0;
|
|
let mut chars = line.chars().peekable();
|
|
|
|
// Skip initial spaces up to min_indent
|
|
while count < min_indent && chars.peek().is_some() {
|
|
match chars.peek() {
|
|
Some(' ') => {
|
|
chars.next();
|
|
count += 1;
|
|
}
|
|
Some('\t') => {
|
|
chars.next();
|
|
count += 4;
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
// Return the remaining characters
|
|
chars.collect::<String>()
|
|
})
|
|
.collect::<Vec<String>>()
|
|
.join("\n")
|
|
}
|
|
|
|
/**
|
|
* Prefix a multiline string with a specified prefix.
|
|
*
|
|
* This function adds the specified prefix to the beginning of each line in the input text.
|
|
*
|
|
* # Arguments
|
|
*
|
|
* * `text` - The multiline string to prefix
|
|
* * `prefix` - The prefix to add to each line
|
|
*
|
|
* # Returns
|
|
*
|
|
* * `String` - The prefixed string
|
|
*
|
|
* # Examples
|
|
*
|
|
* ```
|
|
* use sal::text::prefix;
|
|
*
|
|
* let text = "line 1\nline 2\nline 3";
|
|
* let prefixed = prefix(text, " ");
|
|
* assert_eq!(prefixed, " line 1\n line 2\n line 3");
|
|
* ```
|
|
*/
|
|
pub fn prefix(text: &str, prefix: &str) -> String {
|
|
text.lines()
|
|
.map(|line| format!("{}{}", prefix, line))
|
|
.collect::<Vec<String>>()
|
|
.join("\n")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_dedent() {
|
|
let indented = " line 1\n line 2\n line 3";
|
|
let dedented = dedent(indented);
|
|
assert_eq!(dedented, "line 1\nline 2\n line 3");
|
|
}
|
|
|
|
#[test]
|
|
fn test_prefix() {
|
|
let text = "line 1\nline 2\nline 3";
|
|
let prefixed = prefix(text, " ");
|
|
assert_eq!(prefixed, " line 1\n line 2\n line 3");
|
|
}
|
|
}
|