摩斯码
1. use std::collections::HashMap;
2. use std::io;
4. const UNKNOWN_CHARACTER: &str = "........";
5. const _UNKNOWN_MORSE_CHARACTER: &str = "_";
7. pub fn encode(message: &str) -> String {
8. let dictionary = _morse_dictionary();
9. message
10. .chars()
11. .into_iter()
12. .map(|char| char.to_uppercase().to_string())
13. .map(|letter| dictionary.get(letter.as_str()))
14. .map(|option| option.unwrap_or(&UNKNOWN_CHARACTER).to_string())
15. .collect::<Vec<String>>()
16. .join(" ")
17. }
19. // Declaritive macro for creating readable map declarations, for more info see https://doc.rust-lang.org/book/ch19-06-macros.html
20. macro_rules! map {
21. ($($key:expr => $value:expr),* $(,)?) => {
22. std::iter::Iterator::collect(std::array::IntoIter::new([$(($key, $value),)*]))
23. };
24. }
26. fn _morse_dictionary() -> HashMap<&'static str, &'static str> {
27. map! {
28. "A" => ".-", "B" => "-...", "C" => "-.-.",
29. "D" => "-..", "E" => ".", "F" => "..-.",
30. "G" => "--.", "H" => "....", "I" => "..",
31. "J" => ".---", "K" => "-.-", "L" => ".-..",
32. "M" => "--", "N" => "-.", "O" => "---",
33. "P" => ".--.", "Q" => "--.-", "R" => ".-.",
34. "S" => "...", "T" => "-", "U" => "..-",
35. "V" => "...-", "W" => ".--", "X" => "-..-",
36. "Y" => "-.--", "Z" => "--..",
38. "1" => ".----", "2" => "..---", "3" => "...--",
39. "4" => "....-", "5" => ".....", "6" => "-....",
40. "7" => "--...", "8" => "---..", "9" => "----.",
41. "0" => "-----",
43. "&" => ".-...", "@" => ".--.-.", ":" => "---...",
44. "," => "--..--", "." => ".-.-.-", "'" => ".----.",
45. "\"" => ".-..-.", "?" => "..--..", "/" => "-..-.",
46. "=" => "-...-", "+" => ".-.-.", "-" => "-....-",
47. "(" => "-.--.", ")" => "-.--.-", " " => "/",
48. "!" => "-.-.--",
49. }
50. }
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",
64. ".----" => "1", "..---" => "2", "...--" => "3",
65. "....-" => "4", "....." => "5", "-...." => "6",
66. "--..." => "7", "---.." => "8", "----." => "9",
67. "-----" => "0",
69. ".-..." => "&", ".--.-." => "@", "---..." => ":",
70. "--..--" => ",", ".-.-.-" => ".", ".----." => "'",
71. ".-..-." => "\"", "..--.." => "?", "-..-." => "/",
72. "-...-" => "=", ".-.-." => "+", "-....-" => "-",
73. "-.--." => "(", "-.--.-" => ")", "/" => " ",
74. "-.-.--" => "!", " " => " ", "" => ""
75. }
76. }
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. }
88. fn _check_all_parts(string: &str) -> bool {
89. string.split('/').all(_check_part)
90. }
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. }
99. fn _decode_part(string: &str) -> String {
100. string
101. .split(' ')
102. .map(_decode_token)
103. .collect::<Vec<String>>()
104. .join("")
105. }
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. }
119. let mut partitions: Vec<String> = vec![];
121. for part in string.split('/') {
122. partitions.push(_decode_part(part));
123. }
125. Ok(partitions.join(" "))
126. }
128. #[cfg(test)]
129. mod tests {
130. use super::*;
132. #[test]
133. fn encrypt_only_letters() {
134. let message = "Hello Morse";
135. let cipher = encode(message);
136. assert_eq!(
137. cipher,
138. ".... . .-.. .-.. --- / -- --- .-. ... .".to_string()
139. )
140. }
142. #[test]
143. fn encrypt_letters_and_special_characters() {
144. let message = "What's a great day!";
145. let cipher = encode(message);
146. assert_eq!(
147. cipher,
148. ".-- .... .- - .----. ... / .- / --. .-. . .- - / -.. .- -.-- -.-.--".to_string()
149. )
150. }
152. #[test]
153. fn encrypt_message_with_unsupported_character() {
154. let message = "Error?? {}";
155. let cipher = encode(message);
156. assert_eq!(
157. cipher,
158. ". .-. .-. --- .-. ..--.. ..--.. / ........ ........".to_string()
159. )
160. }
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();
170. assert_eq!(expected, result);
171. }
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. );
184. let encypted = ".-.-.--.-.-. --------. ..---.-.-. .-.-.--.-.-. / .-.-.--.-.-.".to_string();
185. let result = decode(&encypted).unwrap();
187. assert_eq!(expected, result);
188. }
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);
196. assert_eq!(expected, result);
197. }
198. }