use num::cast::AsPrimitive;
use std::ops::{Deref, Rem};
#[derive(Clone, Copy)]
pub struct Caesar {
shift: u8,
}
impl Caesar {
pub fn new<U: AsPrimitive<u8> + Rem>(shift: U) -> Self {
Caesar {
shift: match shift.as_() {
0..=26 => shift.as_(),
_ => shift.as_() % 26,
},
}
}
pub fn encrypt<S: Deref<Target = str>>(self, buf: S) -> String {
let chars = buf.as_bytes();
let vec: Vec<u8> = chars
.iter()
.map(|c| match c {
b'a'..=b'z' => {
let pos = c % 97;
97 + ((pos + self.shift) % 26)
}
b'A'..=b'Z' => {
let pos = c % 65;
65 + ((pos + self.shift) % 26)
}
_ => *c,
})
.collect();
unsafe { String::from_utf8_unchecked(vec) }
}
pub fn encrypt_bytes(self, chars: &mut [u8]) {
for c in chars {
*c = match *c {
b'a'..=b'z' => {
let pos = *c % 97;
97 + ((pos + self.shift) % 26)
}
b'A'..=b'Z' => {
let pos = *c % 65;
65 + ((pos + self.shift) % 26)
}
_ => *c,
}
}
}
pub fn decrypt<S: Deref<Target = str>>(self, buf: S) -> String {
let chars = buf.as_bytes();
let vec: Vec<u8> = chars
.iter()
.map(|c| match c {
b'a'..=b'z' => {
let pos = c % 97;
122 - (((25 - pos) + self.shift) % 26)
}
b'A'..=b'Z' => {
let pos = c % 65;
90 - (((25 - pos) + self.shift) % 26)
}
_ => *c,
})
.collect();
unsafe { String::from_utf8_unchecked(vec) }
}
pub fn decrypt_bytes(self, chars: &mut [u8]) {
for c in chars {
*c = match *c {
b'a'..=b'z' => {
let pos = *c % 97;
122 - (((25 - pos) + self.shift) % 26)
}
b'A'..=b'Z' => {
let pos = *c % 65;
90 - (((25 - pos) + self.shift) % 26)
}
_ => *c,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decrypt_basic() {
let key: u8 = 10;
let caesar = Caesar::new(key);
let input = String::from("Drsc sc k coxdoxmo");
let output = String::from("This is a sentence");
assert_eq!(caesar.decrypt(input), output);
}
#[test]
fn encrypt_basic() {
let key: u8 = 20;
let caesar = Caesar::new(key);
let input = String::from("Tests are important");
let output = String::from("Nymnm uly cgjilnuhn");
assert_eq!(caesar.encrypt(input), output);
}
#[test]
fn emoji_passthrough_decrypt() {
let key: u8 = 15;
let caesar = Caesar::new(key);
let input = "😀 😁 😂 🤣 😃 😄 😅 😆 😉 😊 😋 😎 😍";
assert_eq!(caesar.decrypt(input), input);
}
#[test]
fn emoji_passthrough_encrypt() {
let key: u8 = 15;
let caesar = Caesar::new(key);
let input = "😀 😁 😂 🤣 😃 😄 😅 😆 😉 😊 😋 😎 😍";
assert_eq!(caesar.encrypt(input), input);
}
#[test]
fn str() {
let key: u8 = 2;
let caesar = Caesar::new(key);
let input = "Hello world!";
let output = "Jgnnq yqtnf!";
assert_eq!(caesar.encrypt(input), output);
}
#[test]
fn slice() {
let key: u8 = 2;
let caesar = Caesar::new(key);
let input = "Top secret message!";
let output = "Vqr ugetgv";
assert_eq!(caesar.encrypt(&input[0..10]), output);
}
#[test]
fn big_shift() {
let key: u8 = 27;
let caesar = Caesar::new(key);
let input = "a";
let output = "b";
assert_eq!(caesar.encrypt(input), output);
}
}