rot13/
lib.rs

1//! # rot13
2
3use std::io;
4use std::io::prelude::*;
5
6/// rot13 mode
7#[derive(Copy, Clone)]
8pub enum Mode {
9    /// shift each letter 13 places to the right (wrapping)
10    Encrypt,
11    /// shift each letter 13 places to the left (wrapping)
12    Decrypt,
13}
14
15/// rot13 on a single byte
16///
17/// ```
18/// use rot13::{rot13_byte, Mode};
19///
20/// let input = b'x';
21///
22/// let encrypted = rot13_byte(Mode::Encrypt, input);
23/// let decrypted = rot13_byte(Mode::Decrypt, encrypted);
24///
25/// assert_eq!(&input, &decrypted);
26/// ```
27pub fn rot13_byte(mode: Mode, byte: u8) -> u8 {
28    // preserve case
29    let a = if byte.is_ascii_uppercase() {
30        b'A'
31    } else {
32        b'a'
33    };
34
35    // map 'a'..'z' to 0..26
36    let alphabet_pos = byte - a;
37
38    // shift by 13 and wrap around 0..26
39    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    // map 0..26 back to a..z
51    a + shifted_pos
52}
53
54/// rot13_slice
55///
56/// ```
57/// use rot13::{rot13_slice, Mode};
58///
59/// let input = b"Hello, World!";
60///
61/// let encrypted = rot13_slice(Mode::Encrypt, input);
62/// let decrypted = rot13_slice(Mode::Decrypt, &encrypted);
63///
64/// assert_eq!(input, decrypted.as_slice());
65/// ```
66pub fn rot13_slice(mode: Mode, input: &[u8]) -> Vec<u8> {
67    input
68        .iter()
69        .map(|&byte| {
70            // only apply rot13 to ascii alphabetic characters
71            if byte.is_ascii_alphabetic() {
72                rot13_byte(mode, byte)
73            } else {
74                byte
75            }
76        })
77        .collect()
78}
79
80/// rot13 from a reader directly into a writer
81///
82/// continuously read bytes into a buffer, apply rot13 and write the resulting bytes
83pub 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}