text_to_sounds/
parser.rs

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
16/// Parse text to sounds
17///
18/// ## Example
19///
20/// ```rust
21/// use text_to_sounds::{parse, SoundKind, Sound};
22///
23/// let sounds = vec![
24///     Sound::new(SoundKind::Th, String::from("Th")),
25///     Sound::new(SoundKind::Undefined, String::from("e")),
26///     Sound::new(SoundKind::Undefined, String::from(" ")),
27///     Sound::new(SoundKind::Ptk, String::from("t")),
28///     Sound::new(SoundKind::Undefined, String::from("e")),
29///     Sound::new(SoundKind::Undefined, String::from("x")),
30///     Sound::new(SoundKind::Ptk, String::from("t")),
31///     Sound::new(SoundKind::Undefined, String::from(" ")),
32///     Sound::new(SoundKind::Dj, String::from("j")),
33///     Sound::new(SoundKind::Undefined, String::from("u")),
34///     Sound::new(SoundKind::Undefined, String::from("s")),
35///     Sound::new(SoundKind::Ptk, String::from("t")),
36///     Sound::new(SoundKind::Undefined, String::from(" ")),
37///     Sound::new(SoundKind::Undefined, String::from("i")),
38///     Sound::new(SoundKind::Undefined, String::from("n")),
39///     Sound::new(SoundKind::Undefined, String::from(" ")),
40///     Sound::new(SoundKind::Ptk, String::from("c")),
41///     Sound::new(SoundKind::Undefined, String::from("a")),
42///     Sound::new(SoundKind::Undefined, String::from("s")),
43///     Sound::new(SoundKind::Undefined, String::from("e")),
44/// ];
45///
46/// assert_eq!(parse("The text just in case"), sounds);
47/// ```
48pub 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}