1
1
use std:: collections:: HashMap ;
2
+ use std:: io;
2
3
3
4
const UNKNOWN_CHARACTER : & str = "........" ;
5
+ const _UNKNOWN_MORSE_CHARACTER: & str = "_" ;
4
6
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 ( ) ;
7
9
message
8
10
. chars ( )
9
11
. into_iter ( )
@@ -21,7 +23,7 @@ macro_rules! map {
21
23
} ;
22
24
}
23
25
24
- fn morse_dictionary ( ) -> HashMap < & ' static str , & ' static str > {
26
+ fn _morse_dictionary ( ) -> HashMap < & ' static str , & ' static str > {
25
27
map ! {
26
28
"A" => ".-" , "B" => "-..." , "C" => "-.-." ,
27
29
"D" => "-.." , "E" => "." , "F" => "..-." ,
@@ -47,14 +49,90 @@ fn morse_dictionary() -> HashMap<&'static str, &'static str> {
47
49
}
48
50
}
49
51
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
+
50
128
#[ cfg( test) ]
51
129
mod tests {
52
130
use super :: * ;
53
131
54
132
#[ test]
55
133
fn encrypt_only_letters ( ) {
56
134
let message = "Hello Morse" ;
57
- let cipher = morse_code ( message) ;
135
+ let cipher = encode ( message) ;
58
136
assert_eq ! (
59
137
cipher,
60
138
".... . .-.. .-.. --- / -- --- .-. ... ." . to_string( )
@@ -64,7 +142,7 @@ mod tests {
64
142
#[ test]
65
143
fn encrypt_letters_and_special_characters ( ) {
66
144
let message = "What's a great day!" ;
67
- let cipher = morse_code ( message) ;
145
+ let cipher = encode ( message) ;
68
146
assert_eq ! (
69
147
cipher,
70
148
".-- .... .- - .----. ... / .- / --. .-. . .- - / -.. .- -.-- -.-.--" . to_string( )
@@ -74,10 +152,47 @@ mod tests {
74
152
#[ test]
75
153
fn encrypt_message_with_unsupported_character ( ) {
76
154
let message = "Error?? {}" ;
77
- let cipher = morse_code ( message) ;
155
+ let cipher = encode ( message) ;
78
156
assert_eq ! (
79
157
cipher,
80
158
". .-. .-. --- .-. ..--.. ..--.. / ........ ........" . to_string( )
81
159
)
82
160
}
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
+ }
83
198
}
0 commit comments