1use crate::scanner::Scanner;
2use crate::sound::{Sound, SoundKind};
3
4fn add_sound_from_two_letters(
5 first_letter: &char,
6 second_letter: &char,
7 kind: SoundKind,
8 sounds: &mut Vec<Sound>,
9) {
10 sounds.push(Sound::new(
11 kind,
12 first_letter.to_string() + &String::from(*second_letter),
13 ));
14}
15
16pub fn parse<T: AsRef<str>>(text: T) -> Vec<Sound> {
49 let text = text.as_ref();
50
51 let mut sounds = vec![];
52
53 if text.is_empty() {
54 return sounds;
55 }
56
57 let text_splited = text.split(' ').collect::<Vec<&str>>();
58
59 for (index, word) in text_splited.iter().enumerate() {
60 let mut scanner = Scanner::new(word);
61
62 while !scanner.is_done() {
63 match scanner.peek() {
64 letter @ ('c' | 'C')
65 if !scanner.is_last() && scanner.is_next_any(vec!['h', 'H']) =>
66 {
67 let next_letter = scanner.peek_next();
68
69 add_sound_from_two_letters(letter, next_letter, SoundKind::Ch, &mut sounds);
70
71 scanner.pop();
72 scanner.pop();
73 }
74 letter @ ('p' | 'P' | 't' | 'T' | 'c' | 'C')
75 if scanner.is_first() || scanner.is_last() =>
76 {
77 if (letter == &'t' || letter == &'T')
78 && !scanner.is_last()
79 && scanner.is_next_any(vec!['h', 'H'])
80 {
81 let next_letter = scanner.peek_next();
82
83 add_sound_from_two_letters(letter, next_letter, SoundKind::Th, &mut sounds);
84
85 scanner.pop();
86 scanner.pop();
87
88 continue;
89 }
90
91 sounds.push(Sound::new(SoundKind::Ptk, letter.to_string()));
92 scanner.pop();
93 }
94 letter @ ('t' | 'T') if scanner.is_next_any(vec!['h', 'H']) => {
95 let next_letter = scanner.peek_next();
96
97 add_sound_from_two_letters(letter, next_letter, SoundKind::Th, &mut sounds);
98
99 scanner.pop();
100 scanner.pop();
101 }
102 letter @ ('w' | 'W') if scanner.is_first() => {
103 sounds.push(Sound::new(SoundKind::W, letter.to_string()));
104 scanner.pop();
105 }
106 letter @ ('v' | 'V') if scanner.is_first() => {
107 sounds.push(Sound::new(SoundKind::V, letter.to_string()));
108 scanner.pop();
109 }
110 letter @ ('n' | 'N')
111 if !scanner.is_last() && scanner.is_next_any(vec!['g', 'G', 'k', 'K']) =>
112 {
113 let next_letter = scanner.peek_next();
114
115 add_sound_from_two_letters(letter, next_letter, SoundKind::Ng, &mut sounds);
116
117 scanner.pop();
118 scanner.pop();
119 }
120 letter @ ('j' | 'J') if scanner.is_first() => {
121 sounds.push(Sound::new(SoundKind::Dj, letter.to_string()));
122 scanner.pop();
123 }
124 _ => {
125 let letter = scanner.pop();
126 sounds.push(Sound::new(SoundKind::Undefined, letter.to_string()));
127 }
128 }
129 }
130
131 if index + 1 != text_splited.len() {
132 sounds.push(Sound::new(SoundKind::Undefined, " ".to_string()));
133 }
134 }
135
136 sounds
137}
138
139#[cfg(test)]
140mod parse {
141 use super::{parse, Sound, SoundKind};
142
143 #[test]
144 fn it_should_parse_empty() {
145 assert_eq!(parse(""), Vec::<Sound>::new());
146 }
147
148 #[test]
149 fn it_should_parse_space() {
150 let sounds = vec![Sound::new(SoundKind::Undefined, String::from(" "))];
151
152 assert_eq!(parse(" "), sounds);
153 }
154
155 #[test]
156 fn it_should_parse_p_and_t() {
157 let sounds = vec![
158 Sound::new(SoundKind::Ptk, String::from("p")),
159 Sound::new(SoundKind::Undefined, String::from("u")),
160 Sound::new(SoundKind::Ptk, String::from("t")),
161 ];
162
163 assert_eq!(parse("put"), sounds);
164 }
165
166 #[test]
167 fn it_should_parse_th_in_the_beginning() {
168 let sounds = vec![
169 Sound::new(SoundKind::Th, String::from("th")),
170 Sound::new(SoundKind::Undefined, String::from("e")),
171 ];
172
173 assert_eq!(parse("the"), sounds);
174 }
175
176 #[test]
177 fn it_should_parse_th_in_the_middle() {
178 let sounds = vec![
179 Sound::new(SoundKind::Ptk, String::from("t")),
180 Sound::new(SoundKind::Undefined, String::from("o")),
181 Sound::new(SoundKind::Undefined, String::from("g")),
182 Sound::new(SoundKind::Undefined, String::from("e")),
183 Sound::new(SoundKind::Th, String::from("th")),
184 Sound::new(SoundKind::Undefined, String::from("e")),
185 Sound::new(SoundKind::Undefined, String::from("r")),
186 ];
187
188 assert_eq!(parse("together"), sounds);
189 }
190
191 #[test]
192 fn it_should_parse_th_and_t() {
193 let sounds = vec![
194 Sound::new(SoundKind::Th, String::from("th")),
195 Sound::new(SoundKind::Undefined, String::from("r")),
196 Sound::new(SoundKind::Undefined, String::from("o")),
197 Sound::new(SoundKind::Undefined, String::from("t")),
198 Sound::new(SoundKind::Undefined, String::from("t")),
199 Sound::new(SoundKind::Undefined, String::from("l")),
200 Sound::new(SoundKind::Undefined, String::from("e")),
201 ];
202
203 assert_eq!(parse("throttle"), sounds);
204 }
205
206 #[test]
207 fn it_should_parse_c_and_t() {
208 let sounds = vec![
209 Sound::new(SoundKind::Ptk, String::from("c")),
210 Sound::new(SoundKind::Undefined, String::from("a")),
211 Sound::new(SoundKind::Ptk, String::from("t")),
212 ];
213
214 assert_eq!(parse("cat"), sounds);
215 }
216
217 #[test]
218 fn it_should_parse_uppercase_c_and_t() {
219 let sounds = vec![
220 Sound::new(SoundKind::Ptk, String::from("C")),
221 Sound::new(SoundKind::Undefined, String::from("a")),
222 Sound::new(SoundKind::Ptk, String::from("t")),
223 ];
224
225 assert_eq!(parse("Cat"), sounds);
226 }
227
228 #[test]
229 fn it_should_parse_text_with_space() {
230 let sounds = vec![
231 Sound::new(SoundKind::Ptk, String::from("p")),
232 Sound::new(SoundKind::Undefined, String::from("u")),
233 Sound::new(SoundKind::Ptk, String::from("t")),
234 Sound::new(SoundKind::Undefined, String::from(" ")),
235 Sound::new(SoundKind::Ptk, String::from("t")),
236 Sound::new(SoundKind::Undefined, String::from("o")),
237 Sound::new(SoundKind::Undefined, String::from("g")),
238 Sound::new(SoundKind::Undefined, String::from("e")),
239 Sound::new(SoundKind::Th, String::from("th")),
240 Sound::new(SoundKind::Undefined, String::from("e")),
241 Sound::new(SoundKind::Undefined, String::from("r")),
242 ];
243
244 assert_eq!(parse("put together"), sounds);
245 }
246
247 #[test]
248 fn it_should_parse_long_sentence_with_different_registrs() {
249 let sounds = vec![
250 Sound::new(SoundKind::Th, String::from("Th")),
251 Sound::new(SoundKind::Undefined, String::from("e")),
252 Sound::new(SoundKind::Undefined, String::from("n")),
253 Sound::new(SoundKind::Undefined, String::from(" ")),
254 Sound::new(SoundKind::Ptk, String::from("P")),
255 Sound::new(SoundKind::Undefined, String::from("u")),
256 Sound::new(SoundKind::Ptk, String::from("T")),
257 Sound::new(SoundKind::Undefined, String::from(" ")),
258 Sound::new(SoundKind::Ptk, String::from("t")),
259 Sound::new(SoundKind::Undefined, String::from("O")),
260 Sound::new(SoundKind::Undefined, String::from("g")),
261 Sound::new(SoundKind::Undefined, String::from("E")),
262 Sound::new(SoundKind::Th, String::from("TH")),
263 Sound::new(SoundKind::Undefined, String::from("e")),
264 Sound::new(SoundKind::Undefined, String::from("r")),
265 ];
266
267 assert_eq!(parse("Then PuT tOgETHer"), sounds);
268 }
269
270 #[test]
271 fn it_should_parse_dj() {
272 let sounds = vec![
273 Sound::new(SoundKind::Dj, String::from("J")),
274 Sound::new(SoundKind::Undefined, String::from("o")),
275 Sound::new(SoundKind::Undefined, String::from("h")),
276 Sound::new(SoundKind::Undefined, String::from("n")),
277 Sound::new(SoundKind::Undefined, String::from(" ")),
278 Sound::new(SoundKind::Undefined, String::from("g")),
279 Sound::new(SoundKind::Undefined, String::from("o")),
280 Sound::new(SoundKind::Ptk, String::from("t")),
281 Sound::new(SoundKind::Undefined, String::from(" ")),
282 Sound::new(SoundKind::Dj, String::from("j")),
283 Sound::new(SoundKind::Undefined, String::from("o")),
284 Sound::new(SoundKind::Undefined, String::from("b")),
285 Sound::new(SoundKind::Undefined, String::from(" ")),
286 Sound::new(SoundKind::Undefined, String::from("i")),
287 Sound::new(SoundKind::Undefined, String::from("n")),
288 Sound::new(SoundKind::Undefined, String::from(" ")),
289 Sound::new(SoundKind::Dj, String::from("J")),
290 Sound::new(SoundKind::Undefined, String::from("a")),
291 Sound::new(SoundKind::Undefined, String::from("n")),
292 Sound::new(SoundKind::Undefined, String::from("u")),
293 Sound::new(SoundKind::Undefined, String::from("a")),
294 Sound::new(SoundKind::Undefined, String::from("r")),
295 Sound::new(SoundKind::Undefined, String::from("y")),
296 ];
297
298 assert_eq!(parse("John got job in January"), sounds);
299 }
300
301 #[test]
302 fn it_should_parse_ch() {
303 let sounds = vec![
304 Sound::new(SoundKind::Undefined, String::from("S")),
305 Sound::new(SoundKind::Undefined, String::from("u")),
306 Sound::new(SoundKind::Ch, String::from("ch")),
307 Sound::new(SoundKind::Undefined, String::from(" ")),
308 Sound::new(SoundKind::Ch, String::from("CH")),
309 Sound::new(SoundKind::Undefined, String::from("o")),
310 Sound::new(SoundKind::Undefined, String::from("o")),
311 Sound::new(SoundKind::Undefined, String::from("s")),
312 Sound::new(SoundKind::Undefined, String::from("e")),
313 Sound::new(SoundKind::Undefined, String::from(" ")),
314 Sound::new(SoundKind::W, String::from("w")),
315 Sound::new(SoundKind::Undefined, String::from("h")),
316 Sound::new(SoundKind::Undefined, String::from("i")),
317 Sound::new(SoundKind::Ch, String::from("Ch")),
318 Sound::new(SoundKind::Undefined, String::from(" ")),
319 Sound::new(SoundKind::Ch, String::from("cH")),
320 Sound::new(SoundKind::Undefined, String::from("e")),
321 Sound::new(SoundKind::Undefined, String::from("a")),
322 Sound::new(SoundKind::Ptk, String::from("p")),
323 ];
324
325 assert_eq!(parse("Such CHoose whiCh cHeap"), sounds);
326 }
327
328 #[test]
329 fn it_should_parse_w_and_v() {
330 let sounds = vec![
331 Sound::new(SoundKind::W, String::from("W")),
332 Sound::new(SoundKind::Undefined, String::from("h")),
333 Sound::new(SoundKind::Undefined, String::from("a")),
334 Sound::new(SoundKind::Ptk, String::from("t")),
335 Sound::new(SoundKind::Undefined, String::from(" ")),
336 Sound::new(SoundKind::Undefined, String::from("i")),
337 Sound::new(SoundKind::Undefined, String::from("s")),
338 Sound::new(SoundKind::Undefined, String::from(" ")),
339 Sound::new(SoundKind::V, String::from("v")),
340 Sound::new(SoundKind::Undefined, String::from("e")),
341 Sound::new(SoundKind::Ptk, String::from("t")),
342 Sound::new(SoundKind::Undefined, String::from(" ")),
343 Sound::new(SoundKind::Undefined, String::from("a")),
344 Sound::new(SoundKind::Undefined, String::from("n")),
345 Sound::new(SoundKind::Undefined, String::from("d")),
346 Sound::new(SoundKind::Undefined, String::from(" ")),
347 Sound::new(SoundKind::W, String::from("w")),
348 Sound::new(SoundKind::Undefined, String::from("e")),
349 Sound::new(SoundKind::Undefined, String::from(" ")),
350 Sound::new(SoundKind::W, String::from("w")),
351 Sound::new(SoundKind::Undefined, String::from("i")),
352 Sound::new(SoundKind::Undefined, String::from("l")),
353 Sound::new(SoundKind::Undefined, String::from("l")),
354 Sound::new(SoundKind::Undefined, String::from(" ")),
355 Sound::new(SoundKind::V, String::from("V")),
356 Sound::new(SoundKind::Undefined, String::from("i")),
357 Sound::new(SoundKind::Undefined, String::from("e")),
358 Sound::new(SoundKind::Undefined, String::from("w")),
359 ];
360
361 assert_eq!(parse("What is vet and we will View"), sounds);
362 }
363
364 #[test]
365 fn it_should_parse_ng() {
366 let sounds = vec![
367 Sound::new(SoundKind::Ptk, String::from("P")),
368 Sound::new(SoundKind::Undefined, String::from("i")),
369 Sound::new(SoundKind::Ng, String::from("nK")),
370 Sound::new(SoundKind::Undefined, String::from(" ")),
371 Sound::new(SoundKind::Undefined, String::from("b")),
372 Sound::new(SoundKind::Undefined, String::from("r")),
373 Sound::new(SoundKind::Undefined, String::from("i")),
374 Sound::new(SoundKind::Ng, String::from("Ng")),
375 Sound::new(SoundKind::Undefined, String::from("i")),
376 Sound::new(SoundKind::Ng, String::from("ng")),
377 Sound::new(SoundKind::Undefined, String::from(" ")),
378 Sound::new(SoundKind::Undefined, String::from("s")),
379 Sound::new(SoundKind::Undefined, String::from("o")),
380 Sound::new(SoundKind::Undefined, String::from("m")),
381 Sound::new(SoundKind::Undefined, String::from("e")),
382 Sound::new(SoundKind::Th, String::from("th")),
383 Sound::new(SoundKind::Undefined, String::from("i")),
384 Sound::new(SoundKind::Ng, String::from("ng")),
385 Sound::new(SoundKind::Undefined, String::from(" ")),
386 Sound::new(SoundKind::Ptk, String::from("t")),
387 Sound::new(SoundKind::Undefined, String::from("o")),
388 Sound::new(SoundKind::Undefined, String::from(" ")),
389 Sound::new(SoundKind::Undefined, String::from("K")),
390 Sound::new(SoundKind::Undefined, String::from("i")),
391 Sound::new(SoundKind::Ng, String::from("NG")),
392 Sound::new(SoundKind::Undefined, String::from(" ")),
393 Sound::new(SoundKind::Ptk, String::from("t")),
394 Sound::new(SoundKind::Undefined, String::from("o")),
395 Sound::new(SoundKind::Undefined, String::from(" ")),
396 Sound::new(SoundKind::Undefined, String::from("d")),
397 Sound::new(SoundKind::Undefined, String::from("r")),
398 Sound::new(SoundKind::Undefined, String::from("i")),
399 Sound::new(SoundKind::Ng, String::from("Nk")),
400 ];
401
402 assert_eq!(parse("PinK briNging something to KiNG to driNk"), sounds);
403 }
404}