cipher_salad/cipher/
vigenere.rs1use crate::{
2 Alphabet, ForeignGraphemesPolicy,
3 cipher::CipherOperation::{self, Decrypt, Encrypt},
4};
5use unicode_segmentation::UnicodeSegmentation;
6
7pub struct Vigenere {
8 foreign_policy: ForeignGraphemesPolicy,
9 alphabet: Alphabet,
10}
11
12#[derive(Debug)]
13pub enum VigenereError {
14 InvalidKey,
15}
16
17impl Vigenere {
18 #[must_use]
19 pub fn new(alphabet: Alphabet, foreign_policy: ForeignGraphemesPolicy) -> Self {
20 Self {
21 foreign_policy,
22 alphabet,
23 }
24 }
25
26 fn cipher(
27 &self,
28 contents: &str,
29 key: &str,
30 operation: CipherOperation,
31 ) -> Result<String, VigenereError> {
32 let mut key_iter = key.graphemes(true).peekable();
33 if key_iter.peek().is_none() {
34 return Err(VigenereError::InvalidKey);
35 }
36 if !key_iter.all(|grapheme| self.alphabet.contains(grapheme)) {
37 return Err(VigenereError::InvalidKey);
38 }
39
40 let approx_length = contents.len();
41 let mut result = String::with_capacity(approx_length);
42
43 let mut contents = contents.graphemes(true);
44 let mut key = key.graphemes(true).cycle();
45
46 loop {
47 let next = contents.next();
48 let Some(plain_grapheme) = next else { break };
49
50 if !self.alphabet.contains(plain_grapheme) {
51 match self.foreign_policy {
52 ForeignGraphemesPolicy::Include => result.push_str(plain_grapheme),
53 ForeignGraphemesPolicy::Exclude => (),
54 }
55 continue;
56 }
57
58 let key_grapheme = key.next().unwrap();
59
60 let plain_index = self.alphabet.index_of(plain_grapheme);
61 let key_index = self.alphabet.index_of(key_grapheme);
62
63 let ciphered = plain_index
64 .zip(key_index)
65 .and_then(|(plain_index, key_index)| {
66 let ciphered_index = match operation {
67 Encrypt => (plain_index + key_index).rem_euclid(self.alphabet.length()),
68 Decrypt => {
69 let plain_index = plain_index as isize;
74 let key_index = key_index as isize;
75 (plain_index - key_index).rem_euclid(self.alphabet.length() as isize)
76 as usize
77 }
78 };
79 self.alphabet.grapheme_at(ciphered_index)
80 })
81 .cloned()
82 .unwrap_or_else(String::new);
83 result.push_str(&ciphered);
84 }
85 Ok(result)
86 }
87
88 pub fn encrypt(&self, contents: &str, key: &str) -> Result<String, VigenereError> {
89 self.cipher(contents, key, CipherOperation::Encrypt)
90 }
91
92 pub fn decrypt(&self, ciphertext: &str, key: &str) -> Result<String, VigenereError> {
93 self.cipher(ciphertext, key, CipherOperation::Decrypt)
94 }
95}