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}