mini_enigma/utils/
letter.rs

1use super::{ConversionError, ROTOR_SIZE_U8};
2
3const ASCII_OFFSET: u8 = 65;
4
5#[repr(u8)]
6#[allow(missing_docs)]
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
8/// Represent a letter in the enigma machine. Essentially a u8 mod 26.
9pub enum Letter {
10    #[default]
11    A = 0,
12    B = 1,
13    C = 2,
14    D = 3,
15    E = 4,
16    F = 5,
17    G = 6,
18    H = 7,
19    I = 8,
20    J = 9,
21    K = 10,
22    L = 11,
23    M = 12,
24    N = 13,
25    O = 14,
26    P = 15,
27    Q = 16,
28    R = 17,
29    S = 18,
30    T = 19,
31    U = 20,
32    V = 21,
33    W = 22,
34    X = 23,
35    Y = 24,
36    Z = 25,
37}
38
39impl core::ops::Add for Letter {
40    type Output = Letter;
41    fn add(self, rhs: Self) -> Self::Output {
42        unsafe { core::mem::transmute::<u8, Letter>((self as u8 + rhs as u8) % ROTOR_SIZE_U8) }
43    }
44}
45impl core::ops::Add<u8> for Letter {
46    type Output = Letter;
47
48    fn add(self, rhs: u8) -> Self::Output {
49        unsafe { core::mem::transmute::<u8, Letter>((self as u8 + rhs) % ROTOR_SIZE_U8) }
50    }
51}
52impl core::ops::Sub for Letter {
53    type Output = Letter;
54
55    fn sub(self, rhs: Self) -> Self::Output {
56        unsafe {
57            core::mem::transmute::<u8, Letter>(
58                (self as u8 + (ROTOR_SIZE_U8 - rhs as u8 % ROTOR_SIZE_U8)) % ROTOR_SIZE_U8,
59            )
60        }
61    }
62}
63impl core::ops::Sub<u8> for Letter {
64    type Output = Letter;
65
66    fn sub(self, rhs: u8) -> Self::Output {
67        unsafe {
68            core::mem::transmute::<u8, Letter>(
69                (self as u8 + (ROTOR_SIZE_U8 - rhs % ROTOR_SIZE_U8)) % ROTOR_SIZE_U8,
70            )
71        }
72    }
73}
74impl core::ops::AddAssign<u8> for Letter {
75    fn add_assign(&mut self, rhs: u8) {
76        *self = *self + rhs;
77    }
78}
79
80impl From<Letter> for char {
81    fn from(value: Letter) -> Self {
82        (value as u8 + ASCII_OFFSET) as char
83    }
84}
85
86impl TryFrom<char> for Letter {
87    type Error = ConversionError;
88
89    fn try_from(value: char) -> Result<Self, Self::Error> {
90        if value > 64 as char && value <= 90 as char {
91            Ok(unsafe { core::mem::transmute::<u8, Letter>(value as u8 - ASCII_OFFSET) })
92        } else {
93            Err(ConversionError)
94        }
95    }
96}
97
98impl From<u8> for Letter {
99    fn from(value: u8) -> Self {
100        unsafe { core::mem::transmute::<u8, Letter>(value % ROTOR_SIZE_U8) }
101    }
102}
103
104impl Letter {
105    /// Create a `Letter` from a `char`
106    ///
107    /// # Panics
108    ///
109    /// Unable to convert char
110    #[must_use]
111    pub const fn const_from_char(value: char) -> Letter {
112        unsafe { core::mem::transmute::<u8, Letter>((value as u8 - ASCII_OFFSET) % ROTOR_SIZE_U8) }
113    }
114
115    /// Create a `Letter` from a `u8`
116    #[must_use]
117    pub const fn const_from_u8(value: u8) -> Letter {
118        unsafe { core::mem::transmute::<u8, Letter>(value % ROTOR_SIZE_U8) }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_add() {
128        assert_eq!(Letter::A + Letter::B, Letter::B);
129        assert_eq!(Letter::Z + Letter::B, Letter::A);
130        assert_eq!(Letter::Y + Letter::B, Letter::Z);
131        assert_eq!(Letter::Y + Letter::E, Letter::C);
132    }
133
134    #[test]
135    fn test_sub() {
136        assert_eq!(Letter::B - Letter::B, Letter::A);
137        assert_eq!(Letter::B - Letter::D, Letter::Y);
138    }
139
140    #[test]
141    fn test_idx() {
142        let arr = [Letter::C, Letter::B, Letter::A];
143        assert_eq!(arr[Letter::B], Letter::B);
144        assert_eq!(arr[Letter::A], Letter::C);
145    }
146}