mini_enigma/components/
plugboard.rs

1use core::str::FromStr;
2
3use crate::utils::{ConversionError, UNIT_LETTER_MAP};
4
5use super::utils::{Letter, LetterMapping};
6
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
8/// The Enigma Plugboard or Steckerbrett
9pub struct Plugboard {
10    steckerverbindungen: LetterMapping,
11}
12
13impl Default for Plugboard {
14    fn default() -> Self {
15        Plugboard {
16            steckerverbindungen: UNIT_LETTER_MAP,
17        }
18    }
19}
20
21impl Plugboard {
22    /// Create a new `Plugboard` from an array of `Letter` pairs that represent cable connections
23    #[must_use]
24    pub fn new(pairs: &[(Letter, Letter)]) -> Self {
25        let mut new = Plugboard::default();
26        pairs.iter().for_each(|&pair| new.add_cable(pair));
27        new
28    }
29
30    /// Resets the value of each `Letter` in the pair.
31    /// Equivalent to the logical removal of a cable iff the pair is connected
32    pub fn remove_cable(&mut self, pair: (Letter, Letter)) {
33        self.steckerverbindungen[pair.0] = pair.0;
34        self.steckerverbindungen[pair.1] = pair.1;
35    }
36
37    /// Set the value of each `Letter` in the pair to the other.
38    /// Equivalent to the logical addition of a cable iff neither value
39    /// is already connected to another value.
40    pub fn add_cable(&mut self, pair: (Letter, Letter)) {
41        debug!("adding {:?} and {:?}", pair.0, pair.1);
42        self.steckerverbindungen[pair.0] = pair.1;
43        self.steckerverbindungen[pair.1] = pair.0;
44    }
45}
46
47impl FromStr for Plugboard {
48    type Err = ConversionError;
49    /// Convert a string of characters (optionally delimited with `,`, `-` or ` `) into a Plugboard
50    fn from_str(string: &str) -> Result<Plugboard, Self::Err> {
51        let mut plugboard = Plugboard::new(&[]);
52        let mut a = string
53            .chars()
54            .filter(|x| ![',', '-', ' '].contains(x))
55            .map(Letter::try_from);
56        while let Some(x) = a.next() {
57            if let Some(y) = a.next() {
58                plugboard.add_cable((x?, y?));
59            }
60        }
61        debug!("{plugboard:?}");
62        Ok(plugboard)
63    }
64}
65
66impl core::ops::Index<Letter> for Plugboard {
67    type Output = Letter;
68
69    fn index(&self, index: Letter) -> &Self::Output {
70        &self.steckerverbindungen[index]
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_new() {
80        let plugboard = Plugboard::new(&[(Letter::A, Letter::B), (Letter::X, Letter::Y)]);
81        assert_eq!(plugboard[Letter::A], Letter::B);
82        assert_eq!(plugboard[Letter::B], Letter::A);
83        assert_eq!(plugboard[Letter::X], Letter::Y);
84        assert_eq!(plugboard[Letter::G], Letter::G);
85    }
86
87    #[test]
88    fn test_string_to_plugboard() {
89        let plugboard = Plugboard::from_str("EN,IG,MA").unwrap();
90        assert_eq!(plugboard[Letter::N], Letter::E);
91        assert_eq!(plugboard[Letter::E], Letter::N);
92    }
93}