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
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
//! # rugenere
//!
//! `rugenere` is a simple vigenère cipher tool written in rust.
//! It can encode and decode text either from the standard input or a file.
//! It can also write the output to a file.

pub mod vigenere {
	/// Cipher modes.
	pub enum CipherMode {
		ENCODE,
		DECODE,
	}

	/// Returns a string with the result of encoding/decoding input with the specified key.
	/// This method calls change_char on each character in the input.
	pub fn do_final(input: &String, key: &str, mode: &CipherMode) -> String {
		let key_chars: Vec<char> = key.chars().collect();
		let mut output = Vec::new();
		let mut i = 0;

		for input_char in input.chars() {
			if !input_char.is_alphabetic() {
				output.push(input_char);
				continue;
			}
			output.push(change_char(
				input_char,
				key_chars[i % key_chars.len()],
				mode,
			));
			i += 1;
		}

		output.into_iter().collect()
	}

	/// Encodes/Decodes one character with one key character
	pub fn change_char(input: char, key_char: char, mode: &CipherMode) -> char {
		let (key_char_corrected, initial_char) = if input.is_lowercase() {
			(key_char.to_ascii_lowercase(), 'a')
		} else if input.is_uppercase() {
			(key_char.to_ascii_uppercase(), 'A')
		} else {
			panic!("character {} is neither lowercase or uppercase", input);
		};

		let mut result = ((input as u8) as i32
			+ ((match mode {
			CipherMode::ENCODE => 1,
			CipherMode::DECODE => -1,
		}) * ((key_char_corrected as u8) - (initial_char as u8)) as i32)) as u8;

		if input.is_ascii_lowercase() {
			result = match result as u8 {
				0...96 => result + 26,
				123...150 => result - 26,
				_ => result,
			}
		} else if input.is_ascii_uppercase() {
			result = match result as u8 {
				0...64 => result + 26,
				91...127 => result - 26,
				_ => result,
			}
		}

		result as char
	}

	#[cfg(test)]
	mod tests {
		use super::*;

		#[test]
		fn test_encode() {
			let input = "ThIS is jUSt aN ExAmPLE".to_string();
			let key = "KeY";
			let result = do_final(&input, &key, &CipherMode::ENCODE);

			assert_eq!(result, "DlGC mq tYQd eL ObYwTJO");
		}

		#[test]
		fn test_decode() {
			let input = "DlGC mq tYQd eL ObYwTJO".to_string();
			let key = "kEy";
			let result = do_final(&input, &key, &CipherMode::DECODE);

			assert_eq!(result, "ThIS is jUSt aN ExAmPLE");
		}

		#[test]
		fn test_bigger_key_than_input_encode() {
			let input = "teSt".to_string();
			let key = "thisIsABIGKey";
			let result = do_final(&input, &key, &CipherMode::ENCODE);

			assert_eq!(result, "mlAl");
		}

		#[test]
		fn test_bigger_key_than_input_decode() {
			let input = "mlAl".to_string();
			let key = "thisIsABIGKey";
			let result = do_final(&input, &key, &CipherMode::DECODE);

			assert_eq!(result, "teSt");
		}
	}
}