scream_cipher/
lib.rs

1//! # `scream_cypher`
2//!
3//! In the **SCREAM CYPHER**, messages consist of all As with different letters
4//! distinguished using diacritics. This is a tool that provides both a CLI
5//! tool and a library to encrypt and decrypt text using the scream cypher.
6//!
7//! ## Acknowledgements
8//!
9//! This cypher originated from [XKCD](https://xkcd.com/3054/). Thank you,
10//! Randall Munroe, for always bringing such beautiful things into this world.
11//!
12//! ## Command line installation and usage
13//!
14//! Install `scream_cypher` with Cargo:
15//!
16//! ```sh
17//! cargo install scream_cypher
18//! ```
19//!
20//! You can then use the `scream` command to encrypt and decrypt messages:
21//!
22//! ```sh
23//! scream encrypt "This is a test."
24//! # Āa̰ảã ảã a āáãā.
25//!
26//! scream decrypt "Āa̰ảã ảã a āáãā."
27//! # This is a test.
28//! ```
29//!
30//! ## Library installation and usage
31//!
32//! Add `scream_cypher` to your project:
33//!
34//! ```sh
35//! cargo add scream_cypher
36//! ```
37//!
38//! You can then use `scream_cypher::encrypt` and `scream_cypher::encrypt` to
39//! encrypt and decrypt messages:
40//!
41//! ```rs
42//! let ciphertext = scream_cipher::encrypt("This is a test.");
43//!
44//! println!("Your message: \"{}\"", ciphertext);
45//! // Your message: "Āa̰ảã ảã a āáãā."
46//!
47//! let plaintext = scream_cipher::decrypt(cyphertext);
48//!
49//! println!("Your message: \"{}\"", plaintext);
50//! // Your message: "This is a test."
51//! ```
52
53use phf::{phf_map, Map};
54
55/// A map of alpha characters to their corresponding scream cypher encoding.
56///
57/// See: <https://xkcd.com/3054/>
58pub const CYPHER_MAP: Map<&'static str, &'static str> = phf_map! {
59    "A" => "A",
60    "B" => "A\u{0307}",
61    "C" => "A\u{0321}",
62    "D" => "A\u{0331}",
63    "E" => "A\u{0301}",
64    "F" => "A\u{032E}",
65    "G" => "A\u{030B}",
66    "H" => "A\u{0330}",
67    "I" => "A\u{0309}",
68    "J" => "A\u{0313}",
69    "K" => "A\u{0323}",
70    "L" => "A\u{0306}",
71    "M" => "A\u{030C}",
72    "N" => "A\u{0302}",
73    "O" => "A\u{030A}",
74    "P" => "A\u{032F}",
75    "Q" => "A\u{0324}",
76    "R" => "A\u{0311}",
77    "S" => "A\u{0303}",
78    "T" => "A\u{0304}",
79    "U" => "A\u{0308}",
80    "V" => "A\u{0300}",
81    "W" => "A\u{030F}",
82    "X" => "A\u{036F}",
83    "Y" => "A\u{0326}",
84    "Z" => "A\u{0337}",
85};
86
87/// Encrypts the given plaintext using the scream cypher.
88///
89/// See: <https://xkcd.com/3054/>
90///
91/// ## Example
92///
93/// ```
94///	let ciphertext = scream_cipher::encrypt("This is a test.");
95///
96/// println!("Your message: \"{}\"", ciphertext);
97/// /// Your message: "Āa̰ảã ảã a āáãā."
98/// # assert_eq!("Āa̰ảã ảã a āáãā.", ciphertext);
99/// ```
100pub fn encrypt(message: &str) -> String {
101    let mut result = message.to_owned();
102
103    for (plain, cypher) in CYPHER_MAP.entries() {
104        result = result
105            .replace(plain, cypher)
106            .replace(&plain.to_lowercase(), &cypher.to_lowercase());
107    }
108
109    result
110}
111
112/// Decrypts the given cyphertext using the scream cypher.
113///
114/// See: <https://xkcd.com/3054/>
115///
116/// ## Example
117///
118/// ```
119///	let plaintext = scream_cipher::decrypt("Āa̰ảã ảã a āáãā.");
120///
121/// println!("Your message: \"{}\"", plaintext);
122/// /// Your message: "This is a test."
123/// # assert_eq!("This is a test.", plaintext);
124/// ```
125pub fn decrypt(message: &str) -> String {
126    let mut result = message.to_owned();
127
128    for (plain, cypher) in CYPHER_MAP.entries() {
129        result = result
130            .replace(cypher, plain)
131            .replace(&cypher.to_lowercase(), &plain.to_lowercase());
132    }
133
134    result
135}
136
137#[cfg(test)]
138mod test {
139    use super::*;
140
141    #[test]
142    fn encrypted_messages_decrypt_to_original_messages() {
143        let messages = vec![
144            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
145            "abcdefghijklmnopqrstuvwxyz",
146            "This is a test of the emergency broadcast system.",
147        ];
148
149        for &message in messages.iter() {
150            assert_eq!(message, decrypt(&encrypt(&message.to_owned())));
151        }
152    }
153}