use crate::common::alphabet::Alphabet;
use crate::common::cipher::Cipher;
use crate::common::{alphabet, substitute};
pub struct Caesar {
shift: usize,
}
impl Cipher for Caesar {
type Key = usize;
type Algorithm = Caesar;
fn new(shift: usize) -> Caesar {
if shift < 1 || shift > 26 {
panic!("The shift factor must be within the range 1 <= n <= 26.");
}
Caesar { shift }
}
fn encrypt(&self, message: &str) -> Result<String, &'static str> {
Ok(substitute::shift_substitution(message, |idx| {
alphabet::STANDARD.modulo((idx + self.shift) as isize)
}))
}
fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
Ok(substitute::shift_substitution(ciphertext, |idx| {
alphabet::STANDARD.modulo(idx as isize - self.shift as isize)
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encrypt_message() {
let c = Caesar::new(2);
assert_eq!("Cvvcem cv fcyp!", c.encrypt("Attack at dawn!").unwrap());
}
#[test]
fn decrypt_message() {
let c = Caesar::new(2);
assert_eq!("Attack at dawn!", c.decrypt("Cvvcem cv fcyp!").unwrap());
}
#[test]
fn with_utf8() {
let c = Caesar::new(3);
let message = "Peace, Freedom and Liberty! 🗡️";
let encrypted = c.encrypt(message).unwrap();
let decrypted = c.decrypt(&encrypted).unwrap();
assert_eq!(decrypted, message);
}
#[test]
fn exhaustive_encrypt() {
let message = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for i in 1..27 {
let c = Caesar::new(i);
let encrypted = c.encrypt(message).unwrap();
let decrypted = c.decrypt(&encrypted).unwrap();
assert_eq!(decrypted, message);
}
}
#[test]
#[should_panic]
fn key_to_small() {
Caesar::new(0);
}
#[test]
#[should_panic]
fn key_to_big() {
Caesar::new(27);
}
}