Skip to main content

running_key/
lib.rs

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}