use std::io::stdin;
use std::io::BufRead;
use std::io::BufReader;
/// Words that will not be capitalized unless they are at the start or end of a title.
const EXCEPTIONS: &[&str] = &[
"vs", "above", "across", "against", "at", "between", "by", "along", "among", "down", "in",
"once", "around", "of", "off", "on", "to", "with", "before", "behind", "below", "beneath",
"down", "from", "near", "toward", "upon", "within", "a", "an", "the", "and", "but", "for",
"or", "nor", "so", "till", "when", "yet", "that", "than", "in", "into", "like", "onto", "over",
"past", "as", "if",
];
/// Here are the rules:
///
/// - Every line is its own title. No multi-line titles.
/// - All words are capitalized, except...
/// - Words are not capitalized if they are EXCEPTIONS, unless...
/// - Words are either the first or the last word.
fn main() {
for line in BufReader::new(stdin()).lines() {
println!("{}", capitalize_line(&line.expect("IO error")));
}
}
/// Capitalizes a single line (title) based on placement and exceptions.
fn capitalize_line(line: &str) -> String {
let mut line = line.split_whitespace().map(str::to_lowercase).peekable();
let mut result: Vec<String> = Vec::new();
// handle first word
let first = line.next().expect("No words on line");
result.push(capitalize_word(&first));
while let Some(word) = line.next() {
let word = if line.peek().is_none() || !EXCEPTIONS.contains(&word.as_str()) {
// capitalize words that are last or aren't in exceptions
capitalize_word(&word)
} else {
// ignore the rest
word
};
result.push(word);
}
// join words into title
result.join(" ")
}
/// Capitalizes a single word.
fn capitalize_word(word: &str) -> String {
let mut chars = word.chars();
let first = chars.next().unwrap();
format!("{}{}", first.to_uppercase(), chars.collect::<String>())
}
#[cfg(test)]
mod tests {
// Note this useful idiom: importing names from outer (for mod tests) scope.
use super::*;
#[test]
fn test_capitalize_word() {
assert_eq!(capitalize_word("word"), String::from("Word"));
assert_eq!(capitalize_word("as"), String::from("As"));
}
#[test]
fn test_capitalize_line() {
assert_eq!(
capitalize_line("this is a test"),
String::from("This Is a Test")
);
assert_eq!(
capitalize_line("similEs: lIke Or As"),
String::from("Similes: like or As")
);
}
}