11use std:: collections:: HashMap ;
2+ use std:: io;
23
34const UNKNOWN_CHARACTER : & str = "........" ;
5+ const _UNKNOWN_MORSE_CHARACTER: & str = "_" ;
46
5- pub fn morse_code ( message : & str ) -> String {
6- let dictionary = morse_dictionary ( ) ;
7+ pub fn encode ( message : & str ) -> String {
8+ let dictionary = _morse_dictionary ( ) ;
79 message
810 . chars ( )
911 . into_iter ( )
@@ -21,7 +23,7 @@ macro_rules! map {
2123 } ;
2224}
2325
24- fn morse_dictionary ( ) -> HashMap < & ' static str , & ' static str > {
26+ fn _morse_dictionary ( ) -> HashMap < & ' static str , & ' static str > {
2527 map ! {
2628 "A" => ".-" , "B" => "-..." , "C" => "-.-." ,
2729 "D" => "-.." , "E" => "." , "F" => "..-." ,
@@ -47,14 +49,90 @@ fn morse_dictionary() -> HashMap<&'static str, &'static str> {
4749 }
4850}
4951
52+ fn _morse_to_alphanumeric_dictionary ( ) -> HashMap < & ' static str , & ' static str > {
53+ map ! {
54+ ".-" => "A" , "-..." => "B" , "-.-." => "C" ,
55+ "-.." => "D" , "." => "E" , "..-." => "F" ,
56+ "--." => "G" , "...." => "H" , ".." => "I" ,
57+ ".---" => "J" , "-.-" => "K" , ".-.." => "L" ,
58+ "--" => "M" , "-." => "N" , "---" => "O" ,
59+ ".--." => "P" , "--.-" => "Q" , ".-." => "R" ,
60+ "..." => "S" , "-" => "T" , "..-" => "U" ,
61+ "...-" => "V" , ".--" => "W" , "-..-" => "X" ,
62+ "-.--" => "Y" , "--.." => "Z" ,
63+
64+ ".----" => "1" , "..---" => "2" , "...--" => "3" ,
65+ "....-" => "4" , "....." => "5" , "-...." => "6" ,
66+ "--..." => "7" , "---.." => "8" , "----." => "9" ,
67+ "-----" => "0" ,
68+
69+ ".-..." => "&" , ".--.-." => "@" , "---..." => ":" ,
70+ "--..--" => "," , ".-.-.-" => "." , ".----." => "'" ,
71+ ".-..-." => "\" " , "..--.." => "?" , "-..-." => "/" ,
72+ "-...-" => "=" , ".-.-." => "+" , "-....-" => "-" ,
73+ "-.--." => "(" , "-.--.-" => ")" , "/" => " " ,
74+ "-.-.--" => "!" , " " => " " , "" => ""
75+ }
76+ }
77+
78+ fn _check_part ( string : & str ) -> bool {
79+ for c in string. chars ( ) {
80+ match c {
81+ '.' | '-' | ' ' => ( ) ,
82+ _ => return false ,
83+ }
84+ }
85+ true
86+ }
87+
88+ fn _check_all_parts ( string : & str ) -> bool {
89+ string. split ( '/' ) . all ( _check_part)
90+ }
91+
92+ fn _decode_token ( string : & str ) -> String {
93+ _morse_to_alphanumeric_dictionary ( )
94+ . get ( string)
95+ . unwrap_or ( & _UNKNOWN_MORSE_CHARACTER)
96+ . to_string ( )
97+ }
98+
99+ fn _decode_part ( string : & str ) -> String {
100+ string
101+ . split ( ' ' )
102+ . map ( _decode_token)
103+ . collect :: < Vec < String > > ( )
104+ . join ( "" )
105+ }
106+
107+ /// Convert morse code to ascii.
108+ ///
109+ /// Given a morse code, return the corresponding message.
110+ /// If the code is invalid, the undecipherable part of the code is replaced by `_`.
111+ pub fn decode ( string : & str ) -> Result < String , io:: Error > {
112+ if !_check_all_parts ( string) {
113+ return Err ( io:: Error :: new (
114+ io:: ErrorKind :: InvalidData ,
115+ "Invalid morse code" ,
116+ ) ) ;
117+ }
118+
119+ let mut partitions: Vec < String > = vec ! [ ] ;
120+
121+ for part in string. split ( '/' ) {
122+ partitions. push ( _decode_part ( part) ) ;
123+ }
124+
125+ Ok ( partitions. join ( " " ) )
126+ }
127+
50128#[ cfg( test) ]
51129mod tests {
52130 use super :: * ;
53131
54132 #[ test]
55133 fn encrypt_only_letters ( ) {
56134 let message = "Hello Morse" ;
57- let cipher = morse_code ( message) ;
135+ let cipher = encode ( message) ;
58136 assert_eq ! (
59137 cipher,
60138 ".... . .-.. .-.. --- / -- --- .-. ... ." . to_string( )
@@ -64,7 +142,7 @@ mod tests {
64142 #[ test]
65143 fn encrypt_letters_and_special_characters ( ) {
66144 let message = "What's a great day!" ;
67- let cipher = morse_code ( message) ;
145+ let cipher = encode ( message) ;
68146 assert_eq ! (
69147 cipher,
70148 ".-- .... .- - .----. ... / .- / --. .-. . .- - / -.. .- -.-- -.-.--" . to_string( )
@@ -74,10 +152,47 @@ mod tests {
74152 #[ test]
75153 fn encrypt_message_with_unsupported_character ( ) {
76154 let message = "Error?? {}" ;
77- let cipher = morse_code ( message) ;
155+ let cipher = encode ( message) ;
78156 assert_eq ! (
79157 cipher,
80158 ". .-. .-. --- .-. ..--.. ..--.. / ........ ........" . to_string( )
81159 )
82160 }
161+
162+ #[ test]
163+ fn decrypt_valid_morsecode_with_spaces ( ) {
164+ let expected = "Hello Morse! How's it goin, \" eh\" ?"
165+ . to_string ( )
166+ . to_uppercase ( ) ;
167+ let encypted = encode ( & expected) ;
168+ let result = decode ( & encypted) . unwrap ( ) ;
169+
170+ assert_eq ! ( expected, result) ;
171+ }
172+
173+ #[ test]
174+ fn decrypt_valid_character_set_invalid_morsecode ( ) {
175+ let expected = format ! (
176+ "{}{}{}{} {}" ,
177+ _UNKNOWN_MORSE_CHARACTER,
178+ _UNKNOWN_MORSE_CHARACTER,
179+ _UNKNOWN_MORSE_CHARACTER,
180+ _UNKNOWN_MORSE_CHARACTER,
181+ _UNKNOWN_MORSE_CHARACTER,
182+ ) ;
183+
184+ let encypted = ".-.-.--.-.-. --------. ..---.-.-. .-.-.--.-.-. / .-.-.--.-.-." . to_string ( ) ;
185+ let result = decode ( & encypted) . unwrap ( ) ;
186+
187+ assert_eq ! ( expected, result) ;
188+ }
189+
190+ #[ test]
191+ fn decrypt_invalid_morsecode_with_spaces ( ) {
192+ let encypted = "1... . .-.. .-.. --- / -- --- .-. ... ." ;
193+ let result = decode ( encypted) . map_err ( |e| e. kind ( ) ) ;
194+ let expected = Err ( io:: ErrorKind :: InvalidData ) ;
195+
196+ assert_eq ! ( expected, result) ;
197+ }
83198}
0 commit comments