vigenere_cipher/
lib.rs

1#![feature(inclusive_range_syntax)]
2
3pub enum Method {
4    Encipher,
5    Decipher,
6}
7
8const ALPHABET: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
9
10fn char_to_pos(input: char) -> usize {
11    ALPHABET.chars().position(|c| input == c).unwrap()
12}
13
14pub fn cipher(input: &str, key: &str, method: Method) -> String {
15    let w = input.to_string().to_uppercase();
16    let k = key.to_string().to_uppercase();
17
18    let key_alphabet_pos = k.chars().map(|c| char_to_pos(c)).collect::<Vec<usize>>();
19
20    // The amount of chars possible at key length
21    let chunk_amount = w.len() as f32 / k.len() as f32;
22
23    // Create ranges from where to where each group/chunk goes
24    //let split_word: Vec<Vec<char>>
25    (0..chunk_amount.ceil() as usize)
26        .map(|chunk| {
27            let lowerbound = chunk * key.len();
28            let maximum = (chunk_amount.ceil() as usize) - 1;
29
30            let upperbound = if chunk == maximum {
31                // Last chunk/group upperbound
32                w.len() - 1
33            } else {
34                // General chunk/group upperbound
35                lowerbound + key.len() - 1
36            };
37
38            // Collect the char positions
39            (lowerbound..=upperbound)
40                .map(|i| char_to_pos(w.chars().nth(i).unwrap()))
41                .collect::<Vec<usize>>()
42        })
43        .collect::<Vec<Vec<usize>>>()
44        .into_iter()
45        .map(|char_chunk| {
46            char_chunk
47                .into_iter()
48                .enumerate()
49                .map(|(i, single)| {
50                    let encrypted_char_pos = match method {
51                        Method::Encipher => (single + key_alphabet_pos[i]) % ALPHABET.len(),
52                        Method::Decipher => {
53                            let pos = single as i8 - key_alphabet_pos[i] as i8;
54                            // If the position is negative, start from the end of the alphabet
55                            if pos < 0 {
56                                (ALPHABET.len() as i8 + pos) as usize
57                            } else {
58                                pos as usize
59                            }
60                        }
61                    };
62                    ALPHABET.chars().nth(encrypted_char_pos).unwrap()
63                })
64                .collect::<String>()
65        })
66        .collect::<Vec<String>>()
67        .concat()
68}