use crate::prelude::ALPHABET;
use crate::Traits::{BruteForce, Decrypt, Encrypt};
#[cfg(feature = "python-integration")]
use pyo3::pyclass;
use rand::prelude::SliceRandom;
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
pub enum MonoError {
InvalidCharacter(char),
}
#[derive(Default)]
#[cfg_attr(feature = "python-integration", pyclass(get_all))]
pub struct Monosubstitution {
pub dict: HashMap<char, char>,
}
impl From<HashMap<char, char>> for Monosubstitution {
fn from(dict: HashMap<char, char>) -> Self {
Self { dict }
}
}
#[cfg(not(feature = "python-integration"))]
impl Monosubstitution {
pub fn new() -> Self {
Self {
dict: Self::generate_dictionary(),
}
}
pub fn generate_dictionary() -> HashMap<char, char> {
let plaintext_chars = ALPHABET.chars().collect::<Vec<_>>();
let mut encrypted_chars = plaintext_chars.clone();
encrypted_chars.shuffle(&mut rand::thread_rng());
let mut dictionary = HashMap::new();
for (plain, encrypted) in plaintext_chars.iter().zip(encrypted_chars.iter()) {
dictionary.insert(*plain, *encrypted);
}
dictionary
}
}
#[cfg(feature = "python-integration")]
mod python_integration {
use super::*;
use pyo3::{prelude::*, pyclass, pymethods, PyResult};
use std::collections::HashMap;
#[pymethods]
impl Monosubstitution {
#[new]
pub fn new_from(dict: HashMap<char, char>) -> Self {
Self { dict }
}
#[staticmethod]
pub fn new() -> Self {
Self {
dict: Self::generate_dictionary(),
}
}
#[staticmethod]
pub fn generate_dictionary() -> HashMap<char, char> {
let plaintext_chars = ALPHABET.chars().collect::<Vec<_>>();
let mut encrypted_chars = plaintext_chars.clone();
encrypted_chars.shuffle(&mut rand::thread_rng());
let mut dictionary = HashMap::new();
for (plain, encrypted) in plaintext_chars.iter().zip(encrypted_chars.iter()) {
dictionary.insert(*plain, *encrypted);
}
dictionary
}
pub fn encrypt(&self, input: String) -> PyResult<String> {
match Encrypt::encrypt(self, input) {
Ok(s) => Ok(s),
Err(e) => Err(pyo3::exceptions::PyException::new_err(format!("{:?}", e))),
}
}
pub fn decrypt(&self, input: String) -> PyResult<String> {
match Decrypt::decrypt(self, input) {
Ok(s) => Ok(s),
Err(e) => Err(pyo3::exceptions::PyException::new_err(format!("{:?}", e))),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::ALPHABET;
#[test]
fn generates_dictionary_with_correct_size() {
let dict = Monosubstitution::generate_dictionary();
let alphabet_chars = ALPHABET.chars().count();
assert_eq!(dict.len(), alphabet_chars);
}
#[test]
fn generates_dictionary_with_unique_values() {
let dict = Monosubstitution::generate_dictionary();
let unique_values: std::collections::HashSet<_> = dict.values().collect();
let alphabet_chars = ALPHABET.chars().count();
assert_eq!(unique_values.len(), alphabet_chars);
}
#[test]
fn generates_dictionary_with_alphabet_keys() {
let dict = Monosubstitution::generate_dictionary();
let keys: std::collections::HashSet<_> = dict.keys().cloned().collect();
let alphabet_set: std::collections::HashSet<_> = ALPHABET.chars().collect();
assert_eq!(keys, alphabet_set);
}
#[test]
fn generates_dictionary_with_alphabet_values() {
let dict = Monosubstitution::generate_dictionary();
let values: std::collections::HashSet<_> = dict.values().cloned().collect();
let alphabet_set: std::collections::HashSet<_> = ALPHABET.chars().collect();
assert_eq!(values, alphabet_set);
}
}