use crate::{
component::EnigmaComponent,
error::{EnigmaError, EnigmaResult},
state::EnigmaState,
stepping::SteppingStrategy,
};
pub struct EnigmaMachine {
plugboard: Box<dyn EnigmaComponent>,
rotors: Vec<Box<dyn EnigmaComponent>>,
reflector: Box<dyn EnigmaComponent>,
stepping: Box<dyn SteppingStrategy>,
}
impl EnigmaMachine {
pub fn new(
plugboard: Box<dyn EnigmaComponent>,
rotors: Vec<Box<dyn EnigmaComponent>>,
reflector: Box<dyn EnigmaComponent>,
stepping: Box<dyn SteppingStrategy>,
) -> EnigmaResult<Self> {
if rotors.is_empty() {
return Err(EnigmaError::InvalidConfiguration(
"at least one rotor is required".into(),
));
}
Ok(Self {
plugboard,
rotors,
reflector,
stepping,
})
}
pub fn process_byte(&self, input: u8, state: &mut EnigmaState) -> EnigmaResult<u8> {
if state.rotor_positions.len() != self.rotors.len() {
return Err(EnigmaError::InvalidState(
"rotor position count does not match rotor count".into(),
));
}
let mut value = self.plugboard.forward(input, state);
for rotor in &self.rotors {
value = rotor.forward(value, state);
}
value = self.reflector.forward(value, state);
for rotor in self.rotors.iter().rev() {
value = rotor.backward(value, state);
}
value = self.plugboard.backward(value, state);
self.stepping
.step(state)
.map_err(EnigmaError::SteppingError)?;
Ok(value)
}
pub fn process_bytes(&self, input: &[u8], state: &mut EnigmaState) -> EnigmaResult<Vec<u8>> {
let mut output = Vec::with_capacity(input.len());
for &byte in input {
output.push(self.process_byte(byte, state)?);
}
Ok(output)
}
}