1use std::io;
4use std::io::prelude::*;
5
6#[derive(Copy, Clone)]
8pub enum Mode {
9 Encrypt,
11 Decrypt,
13}
14
15pub fn rot13_byte(mode: Mode, byte: u8) -> u8 {
28 let a = if byte.is_ascii_uppercase() {
30 b'A'
31 } else {
32 b'a'
33 };
34
35 let alphabet_pos = byte - a;
37
38 let shifted_pos = match mode {
40 Mode::Encrypt => (alphabet_pos + 13) % 26,
41 Mode::Decrypt => {
42 if alphabet_pos < 13 {
43 26 - (13 - alphabet_pos)
44 } else {
45 alphabet_pos - 13
46 }
47 }
48 };
49
50 a + shifted_pos
52}
53
54pub fn rot13_slice(mode: Mode, input: &[u8]) -> Vec<u8> {
67 input
68 .iter()
69 .map(|&byte| {
70 if byte.is_ascii_alphabetic() {
72 rot13_byte(mode, byte)
73 } else {
74 byte
75 }
76 })
77 .collect()
78}
79
80pub fn rot13<R: Read, W: Write>(mode: Mode, input: &mut R, output: &mut W) -> io::Result<()> {
84 let mut buffer = [0; 1024];
85
86 loop {
87 let amount = input.read(&mut buffer)?;
88
89 if amount == 0 {
90 break;
91 }
92
93 output.write_all(rot13_slice(mode, &buffer[..amount]).as_ref())?;
94 }
95
96 Ok(())
97}
98
99#[cfg(test)]
100mod tests {
101 #[test]
102 fn rot13_byte_works() {
103 use super::{rot13_byte, Mode};
104
105 let input = b'A';
106
107 let encrypted = rot13_byte(Mode::Encrypt, input);
108 let decrypted = rot13_byte(Mode::Decrypt, encrypted);
109
110 assert_eq!(input, decrypted);
111 }
112
113 #[test]
114 fn rot13_slice_works() {
115 use super::{rot13_slice, Mode};
116
117 let input = b"Hello, World!";
118
119 let encrypted = rot13_slice(Mode::Encrypt, input);
120 let decrypted = rot13_slice(Mode::Decrypt, &encrypted);
121
122 assert_eq!(input, &decrypted.as_slice())
123 }
124}