mini-enigma 0.3.0

#[no-std] (and no alloc) zero dependency implementation of the M3 Enigma
Documentation
use super::{ConversionError, ROTOR_SIZE_U8};

const ASCII_OFFSET: u8 = 65;

#[repr(u8)]
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
/// Represent a letter in the enigma machine. Essentially a u8 mod 26.
pub enum Letter {
    #[default]
    A = 0,
    B = 1,
    C = 2,
    D = 3,
    E = 4,
    F = 5,
    G = 6,
    H = 7,
    I = 8,
    J = 9,
    K = 10,
    L = 11,
    M = 12,
    N = 13,
    O = 14,
    P = 15,
    Q = 16,
    R = 17,
    S = 18,
    T = 19,
    U = 20,
    V = 21,
    W = 22,
    X = 23,
    Y = 24,
    Z = 25,
}

impl core::ops::Add for Letter {
    type Output = Letter;
    fn add(self, rhs: Self) -> Self::Output {
        unsafe { core::mem::transmute::<u8, Letter>((self as u8 + rhs as u8) % ROTOR_SIZE_U8) }
    }
}
impl core::ops::Add<u8> for Letter {
    type Output = Letter;

    fn add(self, rhs: u8) -> Self::Output {
        unsafe { core::mem::transmute::<u8, Letter>((self as u8 + rhs) % ROTOR_SIZE_U8) }
    }
}
impl core::ops::Sub for Letter {
    type Output = Letter;

    fn sub(self, rhs: Self) -> Self::Output {
        unsafe {
            core::mem::transmute::<u8, Letter>(
                (self as u8 + (ROTOR_SIZE_U8 - rhs as u8 % ROTOR_SIZE_U8)) % ROTOR_SIZE_U8,
            )
        }
    }
}
impl core::ops::Sub<u8> for Letter {
    type Output = Letter;

    fn sub(self, rhs: u8) -> Self::Output {
        unsafe {
            core::mem::transmute::<u8, Letter>(
                (self as u8 + (ROTOR_SIZE_U8 - rhs % ROTOR_SIZE_U8)) % ROTOR_SIZE_U8,
            )
        }
    }
}
impl core::ops::AddAssign<u8> for Letter {
    fn add_assign(&mut self, rhs: u8) {
        *self = *self + rhs;
    }
}

impl From<Letter> for char {
    fn from(value: Letter) -> Self {
        (value as u8 + ASCII_OFFSET) as char
    }
}

impl TryFrom<char> for Letter {
    type Error = ConversionError;

    fn try_from(value: char) -> Result<Self, Self::Error> {
        if value > 64 as char && value <= 90 as char {
            Ok(unsafe { core::mem::transmute::<u8, Letter>(value as u8 - ASCII_OFFSET) })
        } else {
            Err(ConversionError)
        }
    }
}

impl From<u8> for Letter {
    fn from(value: u8) -> Self {
        unsafe { core::mem::transmute::<u8, Letter>(value % ROTOR_SIZE_U8) }
    }
}

impl Letter {
    /// Create a `Letter` from a `char`
    ///
    /// # Panics
    ///
    /// Unable to convert char
    #[must_use]
    pub const fn const_from_char(value: char) -> Letter {
        unsafe { core::mem::transmute::<u8, Letter>((value as u8 - ASCII_OFFSET) % ROTOR_SIZE_U8) }
    }

    /// Create a `Letter` from a `u8`
    #[must_use]
    pub const fn const_from_u8(value: u8) -> Letter {
        unsafe { core::mem::transmute::<u8, Letter>(value % ROTOR_SIZE_U8) }
    }
}

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

    #[test]
    fn test_add() {
        assert_eq!(Letter::A + Letter::B, Letter::B);
        assert_eq!(Letter::Z + Letter::B, Letter::A);
        assert_eq!(Letter::Y + Letter::B, Letter::Z);
        assert_eq!(Letter::Y + Letter::E, Letter::C);
    }

    #[test]
    fn test_sub() {
        assert_eq!(Letter::B - Letter::B, Letter::A);
        assert_eq!(Letter::B - Letter::D, Letter::Y);
    }

    #[test]
    fn test_idx() {
        let arr = [Letter::C, Letter::B, Letter::A];
        assert_eq!(arr[Letter::B], Letter::B);
        assert_eq!(arr[Letter::A], Letter::C);
    }
}