From 2aaa4a65710e4f5b293d3682e09c02b70cd295ed Mon Sep 17 00:00:00 2001 From: "Adam T. Carpenter" Date: Thu, 4 Mar 2021 21:44:24 -0500 Subject: faster exception lookups, slightly fuglier function signatures --- src/main.rs | 78 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index b060ed1..c9f03c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ +use std::collections::HashSet; use std::env::args; use std::io::{stdin, BufRead, BufReader}; +const HELP_MSG: &str = "Title-Case Tool.\n-i | --ignore [word...]\tIgnores a list of words that should not be altered."; + /// 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", @@ -28,7 +31,7 @@ fn main() { break; } "--help" | "-h" => { - println!("Title-Case Tool.\n-i | --ignore [word...]\tIgnores a list of words that should not be altered."); + println!("{}", HELP_MSG); } unrecognized => { panic!(format!("Unrecognized option: {}", unrecognized)); @@ -36,40 +39,54 @@ fn main() { } } + // gather exceptions + let exceptions: HashSet<&'static str> = EXCEPTIONS.iter().cloned().collect(); + + // read input and correct lines for line in BufReader::new(stdin()).lines() { let line = line.expect("IO error"); - println!("{}", correct_line(&line, &ignorables)); + println!("{}", correct_line(&line, &ignorables, &exceptions)); } } /// Corrects a single line (title) based on placement and exceptions. -fn correct_line(line: &str, ignorables: &[String]) -> String { +fn correct_line(line: &str, ignorables: &[String], exceptions: &HashSet<&'static str>) -> String { let mut line = line.split_whitespace().peekable(); - let mut result = Vec::new(); + let mut result = String::new(); if let Some(first) = line.next() { // always correct the first word - result.push(correct_word(first, true, ignorables)); + result.push_str(&correct_word(first, true, ignorables, exceptions)); } else { - // handle empty lines - return String::new(); + // skip empty lines + return result; } // handle every other word while let Some(word) = line.next() { - result.push(correct_word(word, line.peek().is_none(), ignorables)); + result.push(' '); + result.push_str(&correct_word( + word, + line.peek().is_none(), + ignorables, + exceptions, + )); } - // join words into title - result.join(" ") + result } /// Corrects a single word based on exceptions and ignorables. -fn correct_word(word: &str, first_or_last: bool, ignorables: &[String]) -> String { +fn correct_word( + word: &str, + first_or_last: bool, + ignorables: &[String], + exceptions: &HashSet<&'static str>, +) -> String { if ignorables.iter().any(|s| s == word) { // leave ignorables untouched word.to_owned() - } else if first_or_last || !EXCEPTIONS.iter().any(|s| **s == word.to_lowercase()) { + } else if first_or_last || !exceptions.contains(&word.to_lowercase().as_str()) { // capitalize words that are first, last, or aren't exceptions capitalize_word(&word.to_lowercase()) } else { @@ -78,9 +95,6 @@ fn correct_word(word: &str, first_or_last: bool, ignorables: &[String]) -> Strin } } -// correct with just ignorables -// correct with ignorables and exceptions - /// Capitalizes the first letter of a single word. fn capitalize_word(word: &str) -> String { let mut chars = word.chars(); @@ -100,12 +114,24 @@ mod tests { #[test] fn test_correct_word() { - assert_eq!(correct_word("Like", false, &[]), String::from("like")); - assert_eq!(correct_word("like", false, &[]), String::from("like")); - assert_eq!(correct_word("like", true, &[]), String::from("Like")); - assert_eq!(correct_word("liKe", false, &[]), String::from("like")); assert_eq!( - correct_word("computers", false, &[]), + correct_word("Like", false, &[], &EXCEPTIONS.iter().cloned().collect()), + String::from("like") + ); + assert_eq!( + correct_word("like", false, &[], &EXCEPTIONS.iter().cloned().collect()), + String::from("like") + ); + assert_eq!( + correct_word("like", true, &[], &EXCEPTIONS.iter().cloned().collect()), + String::from("Like") + ); + assert_eq!( + correct_word("liKe", false, &[], &EXCEPTIONS.iter().cloned().collect()), + String::from("like") + ); + assert_eq!( + correct_word("computers", false, &[], &HashSet::new()), String::from("Computers") ); } @@ -113,11 +139,15 @@ mod tests { #[test] fn test_correct_line() { assert_eq!( - correct_line("this is a test", &[]), + correct_line("this is a test", &[], &EXCEPTIONS.iter().cloned().collect()), String::from("This Is a Test") ); assert_eq!( - correct_line("similEs: lIke Or As", &[]), + correct_line( + "similEs: lIke Or As", + &[], + &EXCEPTIONS.iter().cloned().collect() + ), String::from("Similes: like or As") ); } @@ -125,13 +155,13 @@ mod tests { #[test] fn test_proper_nouns_acronyms() { assert_eq!( - correct_line("FreeBSD", &[String::from("FreeBSD")]), + correct_line("FreeBSD", &[String::from("FreeBSD")], &HashSet::new()), String::from("FreeBSD") ); } #[test] fn test_empty_lines() { - assert_eq!(correct_line("", &[]), String::from("")); + assert_eq!(correct_line("", &[], &HashSet::new()), String::from(""),); } } -- cgit v1.2.3