摩斯码

  1. use std::collections::HashMap;
  2. use std::io;
  3. const UNKNOWN_CHARACTER: &str = "........";
  4. const _UNKNOWN_MORSE_CHARACTER: &str = "_";
  5. pub fn encode(message: &str) -> String {
  6. let dictionary = _morse_dictionary();
  7. message
  8. .chars()
  9. .into_iter()
  10. .map(|char| char.to_uppercase().to_string())
  11. .map(|letter| dictionary.get(letter.as_str()))
  12. .map(|option| option.unwrap_or(&UNKNOWN_CHARACTER).to_string())
  13. .collect::<Vec<String>>()
  14. .join(" ")
  15. }
  16. // Declaritive macro for creating readable map declarations, for more info see https://doc.rust-lang.org/book/ch19-06-macros.html
  17. macro_rules! map {
  18. ($($key:expr => $value:expr),* $(,)?) => {
  19. std::iter::Iterator::collect(std::array::IntoIter::new([$(($key, $value),)*]))
  20. };
  21. }
  22. fn _morse_dictionary() -> HashMap<&'static str, &'static str> {
  23. map! {
  24. "A" => ".-", "B" => "-...", "C" => "-.-.",
  25. "D" => "-..", "E" => ".", "F" => "..-.",
  26. "G" => "--.", "H" => "....", "I" => "..",
  27. "J" => ".---", "K" => "-.-", "L" => ".-..",
  28. "M" => "--", "N" => "-.", "O" => "---",
  29. "P" => ".--.", "Q" => "--.-", "R" => ".-.",
  30. "S" => "...", "T" => "-", "U" => "..-",
  31. "V" => "...-", "W" => ".--", "X" => "-..-",
  32. "Y" => "-.--", "Z" => "--..",
  33. "1" => ".----", "2" => "..---", "3" => "...--",
  34. "4" => "....-", "5" => ".....", "6" => "-....",
  35. "7" => "--...", "8" => "---..", "9" => "----.",
  36. "0" => "-----",
  37. "&" => ".-...", "@" => ".--.-.", ":" => "---...",
  38. "," => "--..--", "." => ".-.-.-", "'" => ".----.",
  39. "\"" => ".-..-.", "?" => "..--..", "/" => "-..-.",
  40. "=" => "-...-", "+" => ".-.-.", "-" => "-....-",
  41. "(" => "-.--.", ")" => "-.--.-", " " => "/",
  42. "!" => "-.-.--",
  43. }
  44. }
  45. fn _morse_to_alphanumeric_dictionary() -> HashMap<&'static str, &'static str> {
  46. map! {
  47. ".-" => "A", "-..." => "B", "-.-." => "C",
  48. "-.." => "D", "." => "E", "..-." => "F",
  49. "--." => "G", "...." => "H", ".." => "I",
  50. ".---" => "J", "-.-" => "K", ".-.." => "L",
  51. "--" => "M", "-." => "N", "---" => "O",
  52. ".--." => "P", "--.-" => "Q", ".-." => "R",
  53. "..." => "S", "-" => "T", "..-" => "U",
  54. "...-" => "V", ".--" => "W", "-..-" => "X",
  55. "-.--" => "Y", "--.." => "Z",
  56. ".----" => "1", "..---" => "2", "...--" => "3",
  57. "....-" => "4", "....." => "5", "-...." => "6",
  58. "--..." => "7", "---.." => "8", "----." => "9",
  59. "-----" => "0",
  60. ".-..." => "&", ".--.-." => "@", "---..." => ":",
  61. "--..--" => ",", ".-.-.-" => ".", ".----." => "'",
  62. ".-..-." => "\"", "..--.." => "?", "-..-." => "/",
  63. "-...-" => "=", ".-.-." => "+", "-....-" => "-",
  64. "-.--." => "(", "-.--.-" => ")", "/" => " ",
  65. "-.-.--" => "!", " " => " ", "" => ""
  66. }
  67. }
  68. fn _check_part(string: &str) -> bool {
  69. for c in string.chars() {
  70. match c {
  71. '.' | '-' | ' ' => (),
  72. _ => return false,
  73. }
  74. }
  75. true
  76. }
  77. fn _check_all_parts(string: &str) -> bool {
  78. string.split('/').all(_check_part)
  79. }
  80. fn _decode_token(string: &str) -> String {
  81. _morse_to_alphanumeric_dictionary()
  82. .get(string)
  83. .unwrap_or(&_UNKNOWN_MORSE_CHARACTER)
  84. .to_string()
  85. }
  86. fn _decode_part(string: &str) -> String {
  87. string
  88. .split(' ')
  89. .map(_decode_token)
  90. .collect::<Vec<String>>()
  91. .join("")
  92. }
  93. /// Convert morse code to ascii.
  94. ///
  95. /// Given a morse code, return the corresponding message.
  96. /// If the code is invalid, the undecipherable part of the code is replaced by `_`.
  97. pub fn decode(string: &str) -> Result<String, io::Error> {
  98. if !_check_all_parts(string) {
  99. return Err(io::Error::new(
  100. io::ErrorKind::InvalidData,
  101. "Invalid morse code",
  102. ));
  103. }
  104. let mut partitions: Vec<String> = vec![];
  105. for part in string.split('/') {
  106. partitions.push(_decode_part(part));
  107. }
  108. Ok(partitions.join(" "))
  109. }
  110. #[cfg(test)]
  111. mod tests {
  112. use super::*;
  113. #[test]
  114. fn encrypt_only_letters() {
  115. let message = "Hello Morse";
  116. let cipher = encode(message);
  117. assert_eq!(
  118. cipher,
  119. ".... . .-.. .-.. --- / -- --- .-. ... .".to_string()
  120. )
  121. }
  122. #[test]
  123. fn encrypt_letters_and_special_characters() {
  124. let message = "What's a great day!";
  125. let cipher = encode(message);
  126. assert_eq!(
  127. cipher,
  128. ".-- .... .- - .----. ... / .- / --. .-. . .- - / -.. .- -.-- -.-.--".to_string()
  129. )
  130. }
  131. #[test]
  132. fn encrypt_message_with_unsupported_character() {
  133. let message = "Error?? {}";
  134. let cipher = encode(message);
  135. assert_eq!(
  136. cipher,
  137. ". .-. .-. --- .-. ..--.. ..--.. / ........ ........".to_string()
  138. )
  139. }
  140. #[test]
  141. fn decrypt_valid_morsecode_with_spaces() {
  142. let expected = "Hello Morse! How's it goin, \"eh\"?"
  143. .to_string()
  144. .to_uppercase();
  145. let encypted = encode(&expected);
  146. let result = decode(&encypted).unwrap();
  147. assert_eq!(expected, result);
  148. }
  149. #[test]
  150. fn decrypt_valid_character_set_invalid_morsecode() {
  151. let expected = format!(
  152. "{}{}{}{} {}",
  153. _UNKNOWN_MORSE_CHARACTER,
  154. _UNKNOWN_MORSE_CHARACTER,
  155. _UNKNOWN_MORSE_CHARACTER,
  156. _UNKNOWN_MORSE_CHARACTER,
  157. _UNKNOWN_MORSE_CHARACTER,
  158. );
  159. let encypted = ".-.-.--.-.-. --------. ..---.-.-. .-.-.--.-.-. / .-.-.--.-.-.".to_string();
  160. let result = decode(&encypted).unwrap();
  161. assert_eq!(expected, result);
  162. }
  163. #[test]
  164. fn decrypt_invalid_morsecode_with_spaces() {
  165. let encypted = "1... . .-.. .-.. --- / -- --- .-. ... .";
  166. let result = decode(encypted).map_err(|e| e.kind());
  167. let expected = Err(io::ErrorKind::InvalidData);
  168. assert_eq!(expected, result);
  169. }
  170. }