1use cipher_utils::alphabet::Alphabet;
2use vigenere_lib::{Vigenere, VigenereBuilder};
3
4pub struct RunningKey {
5 alphabet: Alphabet,
6 key: String,
7}
8
9impl RunningKey {
10 pub fn encrypt(&self, plaintext: &str) -> anyhow::Result<String> {
11 if self.key.len() < plaintext.len() {
12 anyhow::bail!("Error encrypting running-key cipher: Plaintext is shorter than key. If this is intentional, consider using a Vigenere cipher.");
13 }
14 let key_bytes = self.key.as_bytes();
15 let mut index = 0;
16 plaintext
17 .chars()
18 .map(|plain_char| {
19 if !plain_char.is_alphabetic() {
20 return Ok(plain_char);
21 }
22 let key_char = key_bytes[index] as char;
23 let plaintext_index = self.alphabet.index_of(plain_char).unwrap();
24 let key_index = self.alphabet.index_of(key_char).unwrap();
25 let result = self.alphabet.letter_at(plaintext_index + key_index - 1);
26 index += 1;
27 Ok(if plain_char.is_uppercase() {
28 result.to_ascii_uppercase()
29 } else {
30 result.to_ascii_lowercase()
31 })
32 })
33 .collect()
34 }
35
36 pub fn decrypt(&self, ciphertext: &str) -> anyhow::Result<String> {
37 if self.key.len() < ciphertext.len() {
38 anyhow::bail!("Error decrypting running-key cipher: Ciphertext is shorter than key. If this is intentional, consider using a Vigenere cipher.");
39 }
40 let key_bytes = self.key.as_bytes();
41 let mut index = 0;
42 ciphertext
43 .chars()
44 .map(|cipher_char| {
45 if !cipher_char.is_alphabetic() {
46 return Ok(cipher_char);
47 }
48 let key_char = key_bytes[index] as char;
49 let ciphertext_index = self.alphabet.index_of(cipher_char).unwrap();
50 let key_index = self.alphabet.index_of(key_char).unwrap();
51 let result = self.alphabet.letter_at(ciphertext_index - key_index + 1);
52 index += 1;
53 Ok(if cipher_char.is_uppercase() {
54 result.to_ascii_uppercase()
55 } else {
56 result.to_ascii_lowercase()
57 })
58 })
59 .collect()
60 }
61}
62
63pub trait RunningKeyBuilder {
64 fn alphabet<T: AsRef<str>>(self, alphabet: T) -> impl RunningKeyBuilder;
65 fn key<T: AsRef<str>>(self, key: T) -> impl RunningKeyBuilder;
66 fn build(self) -> anyhow::Result<Vigenere>;
67}
68
69#[derive(Debug, Default)]
70struct IncompleteRunningKey {
71 key: Option<String>,
72 alphabet: Option<Alphabet>,
73}
74
75impl RunningKeyBuilder for anyhow::Result<IncompleteRunningKey> {
76 fn key<T: AsRef<str>>(self, key: T) -> impl RunningKeyBuilder {
77 if let Ok(mut running_key) = self {
78 running_key.key = Some(key.as_ref().to_owned());
79 Ok(running_key)
80 } else {
81 self
82 }
83 }
84
85 fn alphabet<T: AsRef<str>>(self, alphabet: T) -> impl RunningKeyBuilder {
86 if let Ok(mut running_key) = self {
87 running_key.alphabet = Some(Alphabet::caseless(alphabet.as_ref())?);
88 Ok(running_key)
89 } else {
90 self
91 }
92 }
93
94 fn build(self) -> anyhow::Result<Vigenere> {
95 if let Ok(running_key) = self {
96 let Some(key) = running_key.key else {
97 anyhow::bail!("Error building RunningKey: No key provided.");
98 };
99
100 let Some(alphabet) = running_key.alphabet else {
101 anyhow::bail!("Error building RunningKey: No alphabet provided.");
102 };
103
104 Ok(Vigenere::new().alphabet(&alphabet.characters().iter().collect::<String>()).key(key).build().unwrap())
105 } else {
106 Err(self.unwrap_err())
107 }
108 }
109}
110
111impl RunningKey {
112 #[allow(clippy::new_ret_no_self)]
113 pub fn new() -> impl RunningKeyBuilder {
114 Ok(IncompleteRunningKey::default())
115 }
116}