1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Contains substitution methods that are used by a variety of ciphers
//!
use super::alphabet;
use super::alphabet::Alphabet;

/// Performs a shift substitution of letters within a piece of text based on the index of them
/// within the alphabet.
///
/// This substitution is defined by the closure `calc_index(ti)`.
///     * ti = the index of the character to shift
///     * note; the closure should shift the value set within the bounds of the standard alphabet
pub fn shift_substitution<F>(text: &str, calc_index: F) -> String
where
    F: Fn(usize) -> usize,
{
    let mut s_text = String::new();
    for c in text.chars() {
        //Find the index of the character in the alphabet (if it exists in there)
        let pos = alphabet::STANDARD.find_position(c);
        match pos {
            Some(pos) => {
                let si = calc_index(pos); //Calculate substitution index
                s_text.push(alphabet::STANDARD.get_letter(si, c.is_uppercase()));
            }
            None => s_text.push(c), //Push non-alphabetic chars 'as-is'
        }
    }

    s_text
}

/// Performs a poly-substitution on a piece of text based on the index of its characters
/// (within the alphabet) and the keystream `k`.
///
/// This substitution is defined by the closure `calc_index(ti, ki)`.
/// Where:
///     * ti = the index of the character to shift
///     * ki = the index of the next key character in the stream
pub fn key_substitution<F>(text: &str, keystream: &str, calc_index: F) -> String
where
    F: Fn(usize, usize) -> usize,
{
    let mut s_text = String::new();
    let mut keystream_iter = keystream.chars().peekable();
    for tc in text.chars() {
        //Find the index of the character in the alphabet (if it exists in there)
        let tpos = alphabet::STANDARD.find_position(tc);
        match tpos {
            Some(ti) => {
                if let Some(kc) = keystream_iter.peek() {
                    if let Some(ki) = alphabet::STANDARD.find_position(*kc) {
                        //Calculate the index and retrieve the letter to substitute
                        let si = calc_index(ti, ki);
                        s_text.push(alphabet::STANDARD.get_letter(si, tc.is_uppercase()));
                    } else {
                        panic!("Keystream contains a non-alphabetic symbol.");
                    }
                } else {
                    panic!("Keystream is not large enough for full substitution of message.");
                }
                keystream_iter.next();
            }
            None => s_text.push(tc), //Push non-alphabetic chars 'as-is'
        }
    }

    s_text
}