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