1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! # Pig Latin
//!
//! `pig_latin` is a utility for applying the rules of Pig Latin to strings.

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn pig_latin_test_upper() {
        assert_eq!(pig_latin("Test"), "Esttay");
    }

    #[test]
    fn pig_latin_test_lower() {
        assert_eq!(pig_latin("test"), "esttay");
    }

    #[test]
    fn pig_latin_test_sentence() {
        assert_eq!(
            pig_latin("This is a Test, containing punctuation."),
            "Isthay isyay ayay Esttay, ontainingcay unctuationpay."
        );
    }

    #[test]
    fn pig_latin_test_my() {
        assert_eq!(pig_latin("my"), "ymay");
    }

    #[test]
    fn pig_latin_test_rhythm() {
        assert_eq!(pig_latin("rhythm"), "ythmrhay");
    }

    #[test]
    fn pig_latin_test_xr() {
        assert_eq!(pig_latin("xray"), "xrayyay");
    }

    #[test]
    fn pig_latin_test_yt() {
        assert_eq!(pig_latin("yttria"), "yttriayay");
    }

    #[test]
    fn pig_latin_test_square() {
        assert_eq!(pig_latin("square"), "aresquay");
    }

    #[test]
    fn depunctuate_test() {
        assert_eq!(depunctuate("test."), ("test", Some('.')));
    }

    #[test]
    fn match_case_test_upper_to_lower() {
        assert_eq!(match_case("Happy", "sad"), "happy");
    }

    #[test]
    fn match_case_test_lower_to_upper() {
        assert_eq!(match_case("sad", "Happy"), "Sad");
    }

    #[test]
    fn match_case_test_no_change() {
        assert_eq!(match_case("casing", "case"), "casing");
    }
}

/// General processor for all input.
///
/// # Examples
///
/// ```
/// let piglatin = pig_latin::pig_latin("This is a Test, containing punctuation.");
///
/// assert_eq!("Isthay isyay ayay Esttay, ontainingcay unctuationpay.", piglatin);
/// ```
pub fn pig_latin(english: &str) -> String {
    let mut pig_latin_output = String::new();

    for line in english.lines() {
        // Splits the input into words
        for word in line.split_whitespace() {
            if !word.chars().next().unwrap().is_ascii_punctuation() {
                // Removes and saves punctuation
                let (word_processing, punctuation) = depunctuate(word);

                // Sends to case-specific processor
                let mut pig_latin_word = if word_processing.starts_with_vowel() {
                    pig_latin_vowel(word_processing)
                } else {
                    pig_latin_consonant(word_processing)
                };

                //Adds pack punctuation
                if let Some(punctuation) = punctuation {
                    pig_latin_word.push(punctuation)
                };

                // Appends word to output after matching cases
                pig_latin_output = pig_latin_output + &match_case(&pig_latin_word, &word) + " ";
            }
        }
        pig_latin_output += "\n";
    }
    // Removes trailing whitespace
    pig_latin_output.trim_end().to_owned()
}

/// Processor for words beginning with consonants
fn pig_latin_consonant(english: &str) -> String {
    if english.starts_with("xr") | english.starts_with("yt") {
        pig_latin_vowel(english)
    } else {
        let pig_latin_iterator = english.chars();

        let mut pig_latin_word = String::new();

        let mut pig_latin_end = String::new();

        // Iterate over letters in word
        for c in pig_latin_iterator {
            match &c {
                'A' | 'a' | 'E' | 'e' | 'I' | 'i' | 'O' | 'o' => {
                    if pig_latin_word.is_empty() {
                        // If this is the first vowel, finish ending and add to word
                        pig_latin_word.push(c);
                        pig_latin_end.push_str("ay");
                    } else {
                        // Otherwise, just add to word
                        pig_latin_word.push(c);
                    }
                }
                'U' | 'u' => {
                    if pig_latin_word.is_empty() {
                        // If this is the first vowel, finish ending and add to word
                        if pig_latin_end != "sq" {
                            pig_latin_word.push(c);
                            pig_latin_end.push_str("ay");
                        } else {
                            pig_latin_end.push(c);
                        }
                    } else {
                        // Otherwise, just add to word
                        pig_latin_word.push(c);
                    }
                }
                'y' | 'Y' => {
                    if pig_latin_word.is_empty() {
                        // If this is the first vowel, finish ending and add to word
                        pig_latin_word.push(c);
                        pig_latin_end.push_str("ay");
                    } else {
                        // Otherwise, just add to word
                        pig_latin_word.push(c);
                    }
                }
                _ => {
                    if pig_latin_end.ends_with("ay") {
                        pig_latin_word.push(c);
                    } else {
                        pig_latin_end.push(c);
                    }
                }
            }
        }
        pig_latin_word.to_lowercase() + &(pig_latin_end.to_lowercase())
    }
}

/// Processor for words beginning with vowels
fn pig_latin_vowel(english: &str) -> String {
    let pig_latin_word = english.to_owned();

    pig_latin_word + "yay"
}

/// Match case of casing to case of case
fn match_case(casing: &str, case: &str) -> String {
    let mut cased = String::new();

    if case.chars().next().unwrap().is_uppercase() && casing.chars().next().unwrap().is_lowercase()
    {
        let mut casing_interator = casing.chars();
        for c in casing_interator.next().unwrap().to_uppercase() {
            cased.push(c);
        }
        for c in casing_interator {
            cased.push(c);
        }
        cased
    } else if case.chars().next().unwrap().is_lowercase()
        && casing.chars().next().unwrap().is_uppercase()
    {
        let mut casing_interator = casing.chars();
        for c in casing_interator.next().unwrap().to_lowercase() {
            cased.push(c);
        }
        for c in casing_interator {
            cased.push(c);
        }
        cased
    } else {
        casing.to_owned()
    }
}

fn depunctuate(word: &str) -> (&str, Option<char>) {
    if word.ends_with_punctuation() {
        let (place, punctuation) = word.char_indices().last().unwrap();
        (&word[..place], Some(punctuation))
    } else {
        (&word[..], None)
    }
}

/// Checks whether a letter is a vowel
fn is_vowel(character: &char) -> bool {
    match character {
        'A' | 'a' | 'E' | 'e' | 'I' | 'i' | 'O' | 'o' | 'U' | 'u' => true,
        _ => false,
    }
}

trait Text {
    fn ends_with_punctuation(&self) -> bool;
    fn starts_with_vowel(&self) -> bool;
    fn has_vowels(&self) -> bool;
}

impl Text for &str {
    fn ends_with_punctuation(&self) -> bool {
        self.chars().last().unwrap().is_ascii_punctuation()
    }
    fn starts_with_vowel(&self) -> bool {
        is_vowel(&self.chars().next().unwrap())
    }
    fn has_vowels(&self) -> bool {
        for c in self.chars() {
            if is_vowel(&c) {
                return true;
            }
        }
        false
    }
}
// use laxy_static;
// use regex::Regex;
//
// pub struct RuleSet<'a> {
//     pub rules: Vec<&'a Rule<'a>>,
// }
//
// impl<'a> RuleSet<'a> {
//     pub fn new(rules: Vec<&'a Rule<'a>>) -> Self {
//         Self { rules }
//     }
//     pub fn apply(&self, text: &str) -> String {
//         let mut output = text.to_owned();
//         for rule in &self.rules {
//             rule.apply(&mut output);
//         }
//         output
//     }
// }
//
// pub struct Rule<'a> {
//     act_on_class: &'a Class<'a>,
//     modifification: &'a Modification<'a>,
//     apply_rules: RuleSet<'a>,
// }
//
// impl<'a> Rule<'a> {
//     pub fn new(act_on_class: &'a Class, modifification: &'a Modification<'a>, apply_rules: RuleSet<'a>) -> Self {
//         Self {
//             act_on_class,
//             modifification,
//             apply_rules,
//         }
//     }
//     fn apply(&self, text: &mut String) -> String {
//         let mut output = String::new();
//         match self.act_on_class {
//             Class::Word(class) => {
//                 for word in text.split_whitespace() {
//                     if class.is_match(word) {
//
//                     }
//                 }
//             }
//             Class::Line(class) => {
//                 for line in text.lines() {
//
//                 }
//             }
//             Class::Body(class) => {
//
//             }
//         }
//         todo!()
//     }
// }
//
// pub enum Class<'a> {
//     Word(&'a regex::Regex),
//     Line(&'a regex::Regex),
//     Body(&'a regex::Regex),
// }
//
// pub enum Modification<'a> {
//     MoveUntil(&'a regex::Regex, &'a Modification<'a>),
//     ReplaceWith(&'a str),
//     Append(&'a str),
//     Prepend(&'a str),
//     MoveToEnd(u32, u32),
//     Lowercase(),
//     Uppercase(),
// }

// pig_latin: Ruleset(pig_latin_vowel, pig_latin_consonant, MoveUntil(punctuation, MoveToEnd(1, 0)))
// pig_latin_vowel: Append("yay")
// pig_latin_consonant: pig_latin_consonant_set: pig_latin_yt_xr, pig_latin_sq, pig_latin_consonant_main, Append("ay")
// pig_latin_consonant_main: MoveUntil(vowel, MoveToEnd(len_until_class, 0))
// pig_latin_yt_xr: Append("y")
// pig_latin_sq: pig_latin_sq_set: pig_latin_squ, pig_latin_sq_main
// pig_latin_squ: MoveUntil("squ", MoveToEnd(len_until_class, 0))