kryptos/ciphers/
vigenere.rs1pub struct Vigenere {
6 key: &'static str,
7}
8
9impl Vigenere {
10 pub fn new(key: &'static str) -> Result<Self, String> {
25 for c in key.chars() {
26 if c.is_alphabetic() {
27 continue;
28 }
29 return Err(String::from("Key must be alphabetic"));
30 }
31 Ok(Vigenere { key })
32 }
33
34 pub fn encipher(&self, plaintext: &str) -> Result<String, &'static str> {
49 Vigenere::transpose(&self.convert_key().unwrap(), plaintext)
50 }
51
52 pub fn decipher(&self, ciphertext: &str) -> Result<String, &'static str> {
67 let mut filter = Vec::new();
68
69 for n in self.convert_key().unwrap() {
70 filter.push((26 - n) % 26);
71 }
72 Vigenere::transpose(&filter, ciphertext)
73 }
74
75 fn transpose(filter: &[u8], text: &str) -> Result<String, &'static str> {
78 let mut filter_index = 0;
79 let mut result = String::new();
80
81 for c in text.chars() {
82 match c as u8 {
83 65..=90 => {
84 result.push(
85 (((c as u8 - 65 + filter[filter_index % filter.len()]) % 26) + 65) as char,
86 );
87 filter_index += 1;
88 }
89 97..=122 => {
90 result.push(
91 (((c as u8 - 97 + filter[filter_index % filter.len()]) % 26) + 97) as char,
92 );
93 filter_index += 1;
94 }
95 _ => result.push(c),
96 }
97 }
98 Ok(result)
99 }
100
101 fn convert_key(&self) -> Result<Vec<u8>, String> {
104 self.key
105 .chars()
106 .map(|c| match c as u8 {
107 65..=90 => Ok((c as u8 - 65) % 26),
108 97..=122 => Ok((c as u8 - 97) % 26),
109 _ => Err(String::from("Invalid character in key")),
110 })
111 .collect()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::Vigenere;
118
119 #[test]
120 fn valid_key() {
121 assert!(Vigenere::new("secret").is_ok());
122 }
123
124 #[test]
125 fn invalid_key() {
126 assert!(Vigenere::new("s3cr3t").is_err());
127 }
128
129 #[test]
130 fn key_conversion() {
131 let v = vec![3, 8, 5, 5, 8, 4];
132 let x = Vigenere::new("diffie").unwrap();
133 assert_eq!(v, x.convert_key().unwrap());
134 }
135
136 #[test]
137 fn invalid_key_conversion() {
138 assert!(Vigenere::convert_key(&Vigenere { key: "dif.fie" }).is_err());
139 }
140
141 #[test]
142 fn encipher() {
143 let v = Vigenere::new("blaise").unwrap();
144 assert_eq!(
145 "tsh ggy ilvm qsv hhqktfc",
146 v.encipher("shh you have you whisper").unwrap()
147 );
148 }
149
150 #[test]
151 fn with_punctuation() {
152 let v = Vigenere::new("blaise").unwrap();
153 assert_eq!(
154 "tsh! ggy ilvm qsv hhqktfc",
155 v.encipher("shh! you have you whisper").unwrap()
156 );
157 }
158
159 #[test]
160 fn with_unicode() {
161 let v = Vigenere::new("babbage").unwrap();
162 assert_eq!(
163 "Eo zpu 🖤 yidrfu mkwtahfs?",
164 v.encipher("Do you 🖤 secret messages?").unwrap()
165 );
166 }
167
168 #[test]
169 fn decipher() {
170 let v = Vigenere::new("blaise").unwrap();
171 assert_eq!(
172 "whispering can still be heard by others",
173 v.decipher("xsiahistno ueo dtqdp cp hmsve my wllfcs")
174 .unwrap()
175 );
176 }
177}