Skip to content

Commit b84e503

Browse files
feat: Add Morse Decoder. (#212)
* Add Morse Decoder
1 parent 358a330 commit b84e503

File tree

4 files changed

+124
-8
lines changed

4 files changed

+124
-8
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
**/*.rs.bk
77
Cargo.lock
88
/.idea/
9+
.vscode

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ These are for demonstration purposes only.
7373

7474
- [x] [Caesar](./src/ciphers/caesar.rs)
7575
- [x] [Vigenère](./src/ciphers/vigenere.rs)
76-
- [ ] [Morse Code](./src/ciphers/morse_code.rs)
76+
- [x] [Morse Code](./src/ciphers/morse_code.rs)
7777
- [ ] Transposition
7878

7979
---

src/ciphers/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ mod rot13;
44
mod vigenere;
55

66
pub use self::caesar::caesar;
7-
pub use self::morse_code::morse_code;
7+
pub use self::morse_code::{decode, encode};
88
pub use self::rot13::rot13;
99
pub use self::vigenere::vigenere;

src/ciphers/morse_code.rs

+121-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use std::collections::HashMap;
2+
use std::io;
23

34
const 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)]
51129
mod 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

Comments
 (0)