pub fn normalize_sentence(text: &str) -> String {
let mut result = String::with_capacity(text.len());
normalize_sentence_into(text, &mut result);
result
}
pub fn normalize_sentence_into(text: &str, buffer: &mut String) {
buffer.clear();
buffer.reserve(text.len());
let mut chars = text.chars().peekable();
let mut prev_was_space = false;
while let Some(ch) = chars.next() {
match ch {
'\r' => {
if chars.peek() == Some(&'\n') {
chars.next(); }
if !prev_was_space {
buffer.push(' ');
prev_was_space = true;
}
}
'\n' => {
if !prev_was_space {
buffer.push(' ');
prev_was_space = true;
}
}
_ if ch.is_whitespace() => {
if !prev_was_space {
buffer.push(' ');
prev_was_space = true;
}
}
_ => {
buffer.push(ch);
prev_was_space = false;
}
}
}
let trimmed = buffer.trim();
if trimmed.len() != buffer.len() {
let trimmed_content = trimmed.to_string();
buffer.clear();
buffer.push_str(&trimmed_content);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_normalize_sentence_basic() {
let input = "This is a\nsentence with\r\nline breaks.";
let expected = "This is a sentence with line breaks.";
assert_eq!(normalize_sentence(input), expected);
}
#[test]
fn test_normalize_sentence_into_buffer_reuse() {
let mut buffer = String::new();
normalize_sentence_into("Line one.\nLine two.", &mut buffer);
assert_eq!(buffer, "Line one. Line two.");
normalize_sentence_into("Different\r\ncontent.", &mut buffer);
assert_eq!(buffer, "Different content.");
}
#[test]
fn test_normalize_sentence_whitespace_collapse() {
let input = "Multiple\n\n\nspaces\r\n\r\n here.";
let result = normalize_sentence(input);
assert_eq!(result, "Multiple spaces here.");
}
#[test]
fn test_normalize_sentence_preserve_interior() {
let input = " Leading and trailing ";
let result = normalize_sentence(input);
assert_eq!(result, "Leading and trailing");
}
#[test]
fn test_normalize_sentence_empty() {
assert_eq!(normalize_sentence(""), "");
assert_eq!(normalize_sentence(" "), "");
}
#[test]
fn test_normalize_sentence_unicode() {
let input = "Unicode\n世界\r\nwith émojis 🦀.";
let expected = "Unicode 世界 with émojis 🦀.";
assert_eq!(normalize_sentence(input), expected);
}
#[test]
fn test_normalize_sentence_tabs() {
let input = "Text\twith\ttabs\there.";
let expected = "Text with tabs here.";
assert_eq!(normalize_sentence(input), expected);
}
#[test]
fn test_normalize_sentence_mixed_whitespace() {
let input = "Mixed\t\n\twhitespace\r\n\there.";
let expected = "Mixed whitespace here.";
assert_eq!(normalize_sentence(input), expected);
}
#[test]
fn test_normalize_sentence_consecutive_tabs() {
let input = "Multiple\t\t\tconsecutive\ttabs.";
let expected = "Multiple consecutive tabs.";
assert_eq!(normalize_sentence(input), expected);
}
}