mod string_man {
// This is needed to handle caller passing you multiple words.
// Although it still won't handle punctuation correctly.
// "Hello world" works, for example, but "Hello world." doesn't.
// I ran out of steam to solve this problem; so I leave it as
// an exercise :). There are many things a caller could pass
// here that probably won't do what you want
pub fn pigify<S>(s: S) -> String where S: AsRef<str> {
let mut output = String::new();
let mut first = true;
for word in s.as_ref().split_whitespace() {
let s = pigify_word_simple(word);
if first {
output += &s;
first = false;
continue;
}
output += " ";
output += &s;
}
output
}
// Simple version of pigifying a word that I would probably do in practice...
fn pigify_word_simple<S>(s: S) -> String
where
S: AsRef<str>,
{
let s = s.as_ref();
let beginning = match s.chars().next() {
Some(c) => c.to_lowercase().to_string(),
None => return "".to_string(),
};
match beginning.as_ref() {
"a" | "e" | "i" | "o" | "u" => s.to_string() + "-hay",
_ => (&s[1..]).to_string() + "-" + &beginning + "ay",
}
}
#[allow(dead_code)]
// Overly complicated version of pigifying a word that is probably a tiny, tiny bit faster...
fn pigify_word_complex<S>(s: S) -> String
where
S: AsRef<str>,
{
// Using this trait give you the `write_str` and `write_char` methods on
// `String`, which are used below (see pigify).
use std::fmt::Write;
// After this line, s will be a `&str`.
let s = s.as_ref();
let beginning = match s.chars().next() {
// Would use `to_lowercase` here, which returns an iterator of chars because
// I guess the lowercase equivalent of some utf-8 characters are actually
// sets of more than one character. So we also call to_string() on the iterator
// to turn it into a String.
Some(c) => c.to_lowercase().to_string(),
// Return an empty string if someone provides an empty S as input.
None => return "".to_string(),
};
// You know the maximum possible size of the String you will be returning; so
// when you allocate it, tell Rust to reserve enough capacity so it
// won't have to do any reallocations as it writes data into it.
let mut output = String::with_capacity(s.len() + beginning.len() + 3);
match beginning.as_ref() {
// This will now match any vowel (uppercase or lowercase) because of the
// call to `to_lowercase()` above, which is I think what you want.
"a" | "e" | "i" | "o" | "u" => {
output.write_str(s).unwrap();
output.write_str("-hay").unwrap();
}
_ => {
// Indexing into a `&str` like this is safe, but only because
// we know from the early return above that s is not empty.
// In general, though, be careful with indexing into a &str
// (or into any slice in general).
output.write_str(&s[1..]).unwrap();
output.write_str("-").unwrap();
output.write_str(&beginning).unwrap();
output.write_str("ay").unwrap();
}
}
// Final note: I don't think any of the unwraps above can ever fail,
// but someone please correct me if I'm wrong.
output
}
}
```