morse_lib/morse/
custom.rs

1use std::{cell::RefCell, ops::Index};
2use crate::{display_chars::DisplayChars, MorseChar, MorseResult, MorseUnit};
3
4#[cfg(feature = "audio")]
5use crate::sound::Sound;
6#[cfg(feature = "audio")]
7use std::{thread, time};
8
9mod iterator;
10use iterator::*;
11
12use super::TMorse;
13
14/// ## Custom language Morse Code (feature).
15#[derive(Debug, PartialEq, Clone)]
16pub struct MorseCustom {
17    morse_str: Vec<MorseChar>,
18    display_as: DisplayChars,
19    #[cfg(feature = "audio")]
20    sound: Sound,
21    from_char_converter: fn(char) -> MorseResult<Vec<MorseUnit>>,
22    into_char_converter: fn(Vec<MorseUnit>) -> MorseResult<char>,
23}
24
25impl TMorse for MorseCustom {
26    /// Parse text into Morse Code.
27    fn parse_text(&mut self, text: &str) -> MorseResult<()> {
28        let mut morse: Vec<MorseChar> = Vec::new();
29
30        for letter in text.chars() {
31            morse.push(MorseChar::from_char(letter, self.from_char_converter)?);
32        }
33
34        self.morse_str = morse;
35        Ok(())
36    }
37
38    /// Parse binary into Morse Code.
39    fn parse_bin(&mut self, bin: &str) -> MorseResult<()> {
40        let words: Vec<&str> = bin.split("0000000").collect();
41
42        for word in words {
43            let letters: Vec<&str> = word.split("000").collect();
44
45            for letter in letters {
46                self.morse_str
47                    .push(MorseChar::from_bin(letter, self.into_char_converter)?);
48            }
49        }
50
51        Ok(())
52    }
53
54    fn len(&self) -> usize {
55        self.morse_str.len()
56    }
57
58    fn remove(&mut self, idx: usize) -> MorseChar {
59        self.morse_str.remove(idx)
60    }
61}
62
63impl Index<usize> for MorseCustom {
64    type Output = MorseChar;
65
66    fn index(&self, idx: usize) -> &Self::Output {
67        &self.morse_str[idx]
68    }
69}
70
71impl MorseCustom {
72    /// Creates custom language Morse Code struct.
73    /// # Examples
74    ///
75    /// ```
76    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult};
77    /// use MorseUnit::{Dot, Line, Whitespace};
78    ///
79    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
80    ///     match letter {
81    ///         'а' | 'А' => Ok(vec![Dot, Line]),
82    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
83    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
84    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
85    ///         ' ' => Ok(vec![Whitespace]),
86    ///           _ => Err(MorseError::InvalidChar)
87    ///     }
88    /// }
89    ///
90    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
91    ///     if letter.len() == 1 && letter[0] == Whitespace {
92    ///         return Ok(' ');
93    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
94    ///         return Ok('а')
95    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
96    ///         return Ok('в');
97    ///     } else if letter.len() == 4 {
98    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
99    ///             return Ok('б');
100    ///         } else {
101    ///             return Ok('г');
102    ///         }
103    ///     } else {
104    ///         Err(MorseError::InvalidMorseSequence)
105    ///     }
106    /// }
107    ///
108    /// let morse_ua = MorseCustom::new(from_char, into_char);
109    /// ```
110    pub fn new(
111        from_char: fn(char) -> MorseResult<Vec<MorseUnit>>,
112        into_char: fn(Vec<MorseUnit>) -> MorseResult<char>,
113    ) -> MorseCustom {
114        MorseCustom {
115            morse_str: Vec::new(),
116            display_as: DisplayChars::default(),
117            #[cfg(feature = "audio")]
118            sound: Sound::default(),
119            from_char_converter: from_char,
120            into_char_converter: into_char,
121        }
122    }
123
124    /// Creates alias for dot in output string.
125    /// # Examples
126    ///
127    /// ```
128    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
129    /// use MorseUnit::{Dot, Line, Whitespace};
130    ///
131    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
132    ///     match letter {
133    ///         'а' | 'А' => Ok(vec![Dot, Line]),
134    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
135    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
136    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
137    ///         ' ' => Ok(vec![Whitespace]),
138    ///           _ => Err(MorseError::InvalidChar)
139    ///     }
140    /// }
141    ///
142    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
143    ///     if letter.len() == 1 && letter[0] == Whitespace {
144    ///         return Ok(' ');
145    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
146    ///         return Ok('а')
147    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
148    ///         return Ok('в');
149    ///     } else if letter.len() == 4 {
150    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
151    ///             return Ok('б');
152    ///         } else {
153    ///             return Ok('г');
154    ///         }
155    ///     } else {
156    ///         Err(MorseError::InvalidMorseSequence)
157    ///     }
158    /// }
159    ///
160    /// let mut morse = MorseCustom::new(from_char, into_char);
161    ///
162    /// morse.parse_text("ба").unwrap();
163    /// morse.dot_as("🔥");
164    ///
165    /// assert_eq!(morse.to_string(), "⚊ 🔥 🔥 🔥   🔥 ⚊");
166    /// ```
167    pub fn dot_as(&mut self, alias: &str) {
168        self.display_as.dot = alias.to_string();
169    }
170    /// Creates alias for line in output string.
171    /// # Examples
172    ///
173    /// ```
174    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
175    /// use MorseUnit::{Dot, Line, Whitespace};
176    ///
177    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
178    ///     match letter {
179    ///         'а' | 'А' => Ok(vec![Dot, Line]),
180    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
181    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
182    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
183    ///         ' ' => Ok(vec![Whitespace]),
184    ///           _ => Err(MorseError::InvalidChar)
185    ///     }
186    /// }
187    ///
188    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
189    ///     if letter.len() == 1 && letter[0] == Whitespace {
190    ///         return Ok(' ');
191    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
192    ///         return Ok('а')
193    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
194    ///         return Ok('в');
195    ///     } else if letter.len() == 4 {
196    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
197    ///             return Ok('б');
198    ///         } else {
199    ///             return Ok('г');
200    ///         }
201    ///     } else {
202    ///         Err(MorseError::InvalidMorseSequence)
203    ///     }
204    /// }
205    ///
206    /// let mut morse = MorseCustom::new( from_char, into_char);
207    ///
208    /// morse.parse_text("ба").unwrap();
209    /// morse.line_as("➖");
210    ///
211    /// assert_eq!(morse.to_string(), "➖ . . .   . ➖");
212    /// ```
213    pub fn line_as(&mut self, alias: &str) {
214        self.display_as.line = alias.to_string();
215    }
216    /// Creates alias for whitespace in output string.
217    /// # Examples
218    ///
219    /// ```
220    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
221    /// use MorseUnit::{Dot, Line, Whitespace};
222    ///
223    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
224    ///     match letter {
225    ///         'а' | 'А' => Ok(vec![Dot, Line]),
226    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
227    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
228    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
229    ///         ' ' => Ok(vec![Whitespace]),
230    ///           _ => Err(MorseError::InvalidChar)
231    ///     }
232    /// }
233    ///
234    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
235    ///     if letter.len() == 1 && letter[0] == Whitespace {
236    ///         return Ok(' ');
237    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
238    ///         return Ok('а')
239    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
240    ///         return Ok('в');
241    ///     } else if letter.len() == 4 {
242    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
243    ///             return Ok('б');
244    ///         } else {
245    ///             return Ok('г');
246    ///         }
247    ///     } else {
248    ///         Err(MorseError::InvalidMorseSequence)
249    ///     }
250    /// }
251    ///
252    /// let mut morse = MorseCustom::new( from_char, into_char);
253    ///
254    /// morse.parse_text("б а").unwrap();
255    /// morse.whitespace_as("🚧");
256    ///
257    /// assert_eq!(morse.to_string(), "⚊ . . .   🚧   . ⚊");
258    /// ```
259    pub fn whitespace_as(&mut self, alias: &str) {
260        self.display_as.whitespace = alias.to_string();
261    }
262
263    /// Play sound that represent Morse Code.
264    /// <div class="warning">
265    /// 
266    /// **Only** available **if "audio"** feature is **enabled.**
267    /// 
268    /// </div>
269    /// 
270    #[cfg(feature = "audio")]
271    pub fn to_beep(&self) {
272        let morse_str = RefCell::new(self.morse_str.clone());
273        for (idx, m_char) in morse_str.borrow_mut().iter_mut().enumerate() {
274            m_char.frequency(self.sound.frequency);
275            m_char.play_speed(self.sound.speed);
276
277            m_char.to_beep();
278
279            // The space between letters is three units
280            if idx < self.morse_str.len() - 1 {
281                thread::sleep(time::Duration::from_secs(3));
282            }
283        }
284    }
285
286    /// Set sound frequency in MHz.
287    /// <div class="warning">
288    /// 
289    /// **Only** available **if "audio"** feature is **enabled.**
290    /// 
291    /// </div>
292    /// 
293    /// # Examples
294    ///
295    /// ```
296    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
297    /// use MorseUnit::{Dot, Line, Whitespace};
298    ///
299    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
300    ///     match letter {
301    ///         'а' | 'А' => Ok(vec![Dot, Line]),
302    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
303    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
304    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
305    ///         ' ' => Ok(vec![Whitespace]),
306    ///           _ => Err(MorseError::InvalidChar)
307    ///     }
308    /// }
309    ///
310    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
311    ///     if letter.len() == 1 && letter[0] == Whitespace {
312    ///         return Ok(' ');
313    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
314    ///         return Ok('а')
315    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
316    ///         return Ok('в');
317    ///     } else if letter.len() == 4 {
318    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
319    ///             return Ok('б');
320    ///         } else {
321    ///             return Ok('г');
322    ///         }
323    ///     } else {
324    ///         Err(MorseError::InvalidMorseSequence)
325    ///     }
326    /// }
327    ///
328    /// let mut morse = MorseCustom::new(from_char, into_char);
329    ///
330    /// morse.parse_text("б а").unwrap();
331    /// morse.frequency(643.0);
332    /// ```
333    #[cfg(feature = "audio")]
334    pub fn frequency(&mut self, frequency: f32) {
335        self.sound.frequency = frequency;
336    }
337
338    /// Set sound speed.
339    /// <div class="warning">
340    /// 
341    /// **Only** available **if "audio"** feature is **enabled.**
342    /// 
343    /// </div>
344    /// 
345    /// * '1' - normal speed
346    /// * '> 1' - faster
347    /// * '< 1' - slower
348    /// # Examples
349    ///
350    /// ```
351    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
352    /// use MorseUnit::{Dot, Line, Whitespace};
353    ///
354    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
355    ///     match letter {
356    ///         'а' | 'А' => Ok(vec![Dot, Line]),
357    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
358    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
359    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
360    ///         ' ' => Ok(vec![Whitespace]),
361    ///           _ => Err(MorseError::InvalidChar)
362    ///     }
363    /// }
364    ///
365    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
366    ///     if letter.len() == 1 && letter[0] == Whitespace {
367    ///         return Ok(' ');
368    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
369    ///         return Ok('а')
370    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
371    ///         return Ok('в');
372    ///     } else if letter.len() == 4 {
373    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
374    ///             return Ok('б');
375    ///         } else {
376    ///             return Ok('г');
377    ///         }
378    ///     } else {
379    ///         Err(MorseError::InvalidMorseSequence)
380    ///     }
381    /// }
382    ///
383    /// let mut morse = MorseCustom::new( from_char, into_char);
384    ///
385    /// morse.parse_text("б а").unwrap();
386    /// morse.play_speed(2.0);
387    /// ```
388    #[cfg(feature = "audio")]
389    pub fn play_speed(&mut self, speed: f32) {
390        self.sound.speed = speed;
391    }
392
393    /// Creates binary-formatted Morse Code.
394    /// # Examples
395    ///
396    /// ```
397    /// use morse_lib::{MorseCustom, MorseUnit, MorseError, MorseResult, TMorse};
398    /// use MorseUnit::{Dot, Line, Whitespace};
399    ///
400    /// fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>>{
401    ///     match letter {
402    ///         'а' | 'А' => Ok(vec![Dot, Line]),
403    ///         'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
404    ///         'в' | 'В' => Ok(vec![Dot, Line, Line]),
405    ///         'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
406    ///         ' ' => Ok(vec![Whitespace]),
407    ///           _ => Err(MorseError::InvalidChar)
408    ///     }
409    /// }
410    ///
411    /// fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
412    ///     if letter.len() == 1 && letter[0] == Whitespace {
413    ///         return Ok(' ');
414    ///     } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
415    ///         return Ok('а')
416    ///     } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
417    ///         return Ok('в');
418    ///     } else if letter.len() == 4 {
419    ///         if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
420    ///             return Ok('б');
421    ///         } else {
422    ///             return Ok('г');
423    ///         }
424    ///     } else {
425    ///         Err(MorseError::InvalidMorseSequence)
426    ///     }
427    /// }
428    ///
429    /// let mut morse = MorseCustom::new(from_char, into_char);
430    ///
431    /// morse.parse_text("б а").unwrap();
432    ///
433    /// assert_eq!(morse.to_bin_str(),"111010101000000010111");
434    /// ```
435    pub fn to_bin_str(&self) -> String {
436        let mut string = String::new();
437
438        for (idx, m_char) in self.morse_str.iter().enumerate() {
439            string.push_str(&m_char.to_bin_str());
440
441            // The space between letters is three units
442            if idx < self.morse_str.len() - 1 {
443                string.push_str("000");
444            }
445        }
446
447        string
448    }
449    /// Convert Morse Code back to text.
450    pub fn to_text(&self) -> String {
451        let mut text = String::new();
452
453        for m_char in &self.morse_str {
454            text.push(m_char.get_letter());
455        }
456
457        text
458    }
459
460    pub fn iter(&self) -> MorseIterator {
461        MorseIterator::init(self)
462    }
463}
464
465impl IntoIterator for MorseCustom {
466    type Item = MorseChar;
467    type IntoIter = MorseIntoIterator;
468
469    fn into_iter(self) -> MorseIntoIterator {
470        MorseIntoIterator { morse: self }
471    }
472}
473
474impl ToString for MorseCustom {
475    /// Return String value of Morse Code.
476    fn to_string(&self) -> String {
477        let mut string = String::new();
478        let morse = RefCell::new(self.morse_str.clone());
479
480        for (idx, m_char) in morse.borrow_mut().iter_mut().enumerate() {
481            m_char.dot_as(&self.display_as.dot);
482            m_char.line_as(&self.display_as.line);
483            m_char.whitespace_as(&self.display_as.whitespace);
484            string.push_str(&m_char.to_string());
485
486            // The space between letters is three units
487            if idx < self.morse_str.len() - 1 {
488                string.push_str("   ");
489            }
490        }
491
492        string
493    }
494}
495
496#[cfg(test)]
497mod morse_tests {
498    use super::*;
499    use crate::morse_unit::MorseUnit::{Dot, Line, Whitespace};
500    use crate::{MorseError, MorseResult, MorseUnit};
501
502    fn from_char(letter: char) -> MorseResult<Vec<MorseUnit>> {
503        println!("{} confver", letter.to_ascii_lowercase());
504        match letter {
505            'а' | 'А' => Ok(vec![Dot, Line]),
506            'б' | 'Б' => Ok(vec![Line, Dot, Dot, Dot]),
507            'в' | 'В' => Ok(vec![Dot, Line, Line]),
508            'г' | 'Г' => Ok(vec![Dot, Dot, Dot, Dot]),
509            ' ' => Ok(vec![Whitespace]),
510            _ => Err(MorseError::InvalidChar),
511        }
512    }
513    fn into_char(letter: Vec<MorseUnit>) -> MorseResult<char> {
514        if letter.len() == 1 && letter[0] == Whitespace {
515            return Ok(' ');
516        } else if letter.len() == 2 && letter[0] == Dot && letter[1] == Line {
517            return Ok('а');
518        } else if letter.len() == 3 && letter[0] == Dot && letter[1] == Line && letter[2] == Line {
519            return Ok('в');
520        } else if letter.len() == 4 {
521            if letter[0] == Line && letter[1] == Dot && letter[2] == Dot && letter[3] == Dot {
522                return Ok('б');
523            } else {
524                return Ok('г');
525            }
526        } else {
527            Err(MorseError::InvalidMorseSequence)
528        }
529    }
530
531    #[test]
532    fn create_from_text_str() {
533        let mut morse = MorseCustom::new(from_char, into_char);
534        morse.parse_text("Ба").unwrap();
535
536        assert_eq!(morse.to_bin_str(), "11101010100010111");
537    }
538
539    #[test]
540    fn create_from_binary_str() {
541        const BIN: &str = "11101010100010111";
542        let mut morse = MorseCustom::new(from_char, into_char);
543
544        morse.parse_bin(BIN).unwrap();
545
546        assert_eq!(morse.to_text(), "ба");
547    }
548
549    #[test]
550    fn to_string() {
551        let mut morse = MorseCustom::new(from_char, into_char);
552        morse.parse_text("ба").unwrap();
553
554        assert_eq!(morse.to_string(), "⚊ . . .   . ⚊");
555    }
556
557    #[test]
558    fn to_bin_str() {
559        let mut morse = MorseCustom::new(from_char, into_char);
560        morse.parse_text("ба").unwrap();
561        println!("{} BIIIN", morse.to_bin_str());
562
563        assert_eq!(morse.to_bin_str(), "11101010100010111");
564    }
565    #[test]
566    fn set_aliases_for_whitespace_lines_and_dots() {
567        let mut morse = MorseCustom::new(from_char, into_char);
568        morse.parse_text("ба ба").unwrap();
569
570        println!("{} TEXXT", morse.to_text());
571
572        morse.dot_as("🔥");
573        morse.line_as("➖");
574        morse.whitespace_as("🚧");
575
576        assert_eq!(
577            morse.to_string(),
578            "➖ 🔥 🔥 🔥   🔥 ➖   🚧   ➖ 🔥 🔥 🔥   🔥 ➖"
579        );
580    }
581}