cipher_salad/cipher/
caesar.rs1use crate::{
2 Alphabet, ForeignGraphemesPolicy,
3 cipher::CipherOperation::{self, Decrypt, Encrypt},
4};
5use unicode_segmentation::UnicodeSegmentation;
6
7pub struct Caesar {
8 foreign_policy: ForeignGraphemesPolicy,
9 alphabet: Alphabet,
10}
11
12impl Caesar {
13 #[must_use]
14 pub fn new(alphabet: Alphabet, foreign_policy: ForeignGraphemesPolicy) -> Self {
15 Self {
16 foreign_policy,
17 alphabet,
18 }
19 }
20
21 fn cipher(&self, contents: &str, key: usize, operation: CipherOperation) -> String {
22 let key = key % self.alphabet.length();
23
24 contents
25 .graphemes(true)
26 .map(|grapheme| (grapheme, self.alphabet.index_of(grapheme)))
27 .filter_map(|(grapheme, index)| match index {
28 Some(grapheme_index) => {
29 let ciphered_index = match operation {
30 Encrypt => (grapheme_index + key).rem_euclid(self.alphabet.length()),
31 Decrypt => (grapheme_index as isize - key as isize)
32 .rem_euclid(self.alphabet.length() as isize)
33 as usize,
34 };
35 self.alphabet.grapheme_at(ciphered_index).cloned()
36 }
37 None => match self.foreign_policy {
38 ForeignGraphemesPolicy::Include => Some(grapheme.to_string()),
39 ForeignGraphemesPolicy::Exclude => Some(String::new()),
40 },
41 })
42 .collect()
43 }
44
45 #[must_use]
46 pub fn encrypt(&self, contents: &str, key: usize) -> String {
47 self.cipher(contents, key, CipherOperation::Encrypt)
48 }
49
50 #[must_use]
51 pub fn decrypt(&self, ciphertext: &str, key: usize) -> String {
52 self.cipher(ciphertext, key, CipherOperation::Decrypt)
53 }
54}