pub fn finalize_current_line(
output: &mut String,
words_after_first: &mut Vec::<&str>,
num_extra_spaces: u32,
) {
let append_spaces = |s: &mut String, num_spaces: u32| {
for _ in 0..num_spaces {
s.push(' ');
}
};
let num_words_after_first = words_after_first.len() as u32;
if num_words_after_first <= 0 {
append_spaces(output, num_extra_spaces);
return;
}
let num_gaps_between_words = num_words_after_first;
let small_gap_size = 1 + num_extra_spaces / num_gaps_between_words;
let big_gap_size = small_gap_size + 1;
let num_big_gaps = num_extra_spaces % num_gaps_between_words;
let mut words = words_after_first.iter();
let mut append_spaces_and_word = |num_spaces: u32, word: &str| {
append_spaces(output, num_spaces);
output.push_str(word);
};
for _ in 0..num_big_gaps {
append_spaces_and_word(big_gap_size, words.next().unwrap());
}
let mut word = words.next().unwrap();
loop {
append_spaces_and_word(small_gap_size, word);
word = match words.next() {
None => break,
Some(w) => w,
};
};
}
pub fn justify(input: &str, line_width: u32) -> String {
let mut words = input.split_whitespace();
let mut curr_word = match words.next() {
None => return "".to_string(),
Some(w) => w,
};
let mut res = String::with_capacity(input.len());
let mut words_after_first = {
let max_words_per_line = (line_width + 1) / 2;
Vec::<&str>::with_capacity(max_words_per_line as usize - 1)
};
loop {
let mut line_remaining_capacity_chars = line_width;
let mut curr_word_iter = curr_word.chars();
let mut ch = curr_word_iter.next().unwrap();
'first_word_of_line: loop {
res.push(ch);
line_remaining_capacity_chars -= 1;
match curr_word_iter.next() {
None => break 'first_word_of_line,
Some(c) => {
ch = c;
if line_remaining_capacity_chars <= 0 {
res.push('\n');
line_remaining_capacity_chars = line_width;
}
}
}
}
'words_after_first_of_line: loop {
curr_word = match words.next() {
None => {
finalize_current_line(&mut res, &mut words_after_first, line_remaining_capacity_chars);
return res;
},
Some(w) => w,
};
let requred_capacity_to_append_curr_word = curr_word.chars().count() as u32 + 1;
if requred_capacity_to_append_curr_word <= line_remaining_capacity_chars {
words_after_first.push(curr_word);
line_remaining_capacity_chars -= requred_capacity_to_append_curr_word;
} else {
finalize_current_line(&mut res, &mut words_after_first, line_remaining_capacity_chars);
res.push('\n');
break 'words_after_first_of_line;
}
}
words_after_first.clear();
}
}
#[cfg(test)]
mod tests {
use super::justify;
#[test]
fn simple() {
let lorem_ipsum = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua";
let test_cases = [
("", 5, ""),
("test", 4, "test"),
("test", 5, "test "),
("test", 1, "t\ne\ns\nt"),
(" 12345 ", 2, "12\n34\n5 "),
(" ", 5, ""),
("a a 123", 4, "a a\n123 "),
("aaa aaa", 5, "aaa \naaa "), ("a a a 12345", 8, "a a a\n12345 "),
("1234567", 3, "123\n456\n7 "),
("12 123456789abc 1", 5, "12 \n12345\n6789a\nbc 1"),
(lorem_ipsum, 12,
"Lorem ipsum\ndolor sit\namet \nconsectetur \nadipiscing \nelit sed do\neiusmod \ntempor \nincididunt \nut labore et\ndolore magna\naliqua "),
(lorem_ipsum, 7,
"Lorem \nipsum \ndolor \nsit \namet \nconsect\netur \nadipisc\ning \nelit \nsed do\neiusmod\ntempor \nincidid\nunt ut\nlabore \net \ndolore \nmagna \naliqua "),
(lorem_ipsum, 35,
"Lorem ipsum dolor sit amet\nconsectetur adipiscing elit sed do\neiusmod tempor incididunt ut labore\net dolore magna aliqua"),
];
for &(input, line_width, expected) in &test_cases {
assert_eq!(
justify(input, line_width), expected,
"input: \"{}\", width: {}", input, line_width
);
}
}
}