use crate::{input, Cipher, CipherResult};
pub struct RailFence {
key: usize,
}
impl RailFence {
pub fn new(key: usize) -> Self {
assert!(key > 0, "`key` must be greater than 0");
Self { key }
}
}
impl Cipher for RailFence {
fn encipher(&self, ptext: &str) -> CipherResult {
input::is_ascii(ptext)?;
let mut ctext = Vec::with_capacity(ptext.len());
let ptext: Vec<u8> = ptext.bytes().collect();
let mut line = 0usize;
while line < self.key - 1 {
let skip = 2 * (self.key - line - 1);
let mut j = 0usize;
let mut i = line;
while i < ptext.len() {
ctext.push(*ptext.get(i).unwrap());
if line == 0 || j % 2 == 0 {
i += skip;
} else {
i += 2 * (self.key - 1) - skip;
}
j += 1;
}
line += 1;
}
for i in (line..ptext.len()).step_by(2 * (self.key - 1)) {
ctext.push(*ptext.get(i).unwrap());
}
Ok(String::from_utf8(ctext).unwrap())
}
fn decipher(&self, ctext: &str) -> CipherResult {
input::is_ascii(ctext)?;
let mut ptext = vec![0u8; ctext.len()];
let ctext: Vec<u8> = ctext.bytes().collect();
let mut k = 0usize;
let mut line = 0usize;
while line < self.key - 1 {
let skip = 2 * (self.key - line - 1);
let mut j = 0usize;
let mut i = line;
while i < ctext.len() {
ptext[i] = *ctext.get(k).unwrap();
k += 1;
if line == 0 || j % 2 == 0 {
i += skip;
} else {
i += 2 * (self.key - 1) - skip;
}
j += 1;
}
line += 1;
}
for i in (line..ctext.len()).step_by(2 * (self.key - 1)) {
ptext[i] = *ctext.get(k).unwrap();
k += 1;
}
Ok(String::from_utf8(ptext).unwrap())
}
}