use std::{
borrow::BorrowMut,
ops::{Index, IndexMut},
};
use bytes::{BufMut, BytesMut};
use itertools::Itertools;
use num_enum::IntoPrimitive;
#[derive(Clone, PartialEq, Debug, Default)]
pub struct MacroStages {
stages: [Vec<String>; MACRO_STAGE_MAX_ID],
}
impl IndexMut<MacroStage> for MacroStages {
fn index_mut(&mut self, index: MacroStage) -> &mut Self::Output {
self.stages[index.as_usize()].borrow_mut()
}
}
impl Index<MacroStage> for MacroStages {
type Output = Vec<String>;
fn index(&self, index: MacroStage) -> &Self::Output {
&self.stages[index.as_usize()]
}
}
impl MacroStages {
pub(crate) fn write(&self, buffer: &mut BytesMut) {
for (index, stage) in self.stages.iter().enumerate() {
if stage.is_empty() {
continue;
}
let macro_stage: MacroStage = index.into();
let be_bytes: [u8; 4] = u32::to_be_bytes(macro_stage.into());
buffer.extend_from_slice(&be_bytes);
buffer.extend_from_slice(stage.iter().join(" ").as_bytes());
buffer.put_u8(0);
}
}
#[must_use]
pub(crate) fn len(&self) -> usize {
let mut accumulator = 0;
for stage in &self.stages {
if stage.is_empty() {
continue;
}
accumulator += MacroStage::CODE_SIZE;
for symbol in stage {
accumulator += symbol.len() + 1;
}
}
accumulator
}
pub fn with_stage<S: ToString>(&mut self, stage: MacroStage, macros: &[S]) {
let stage = &mut self[stage];
for m in macros {
stage.push(m.to_string());
}
}
}
const MACRO_STAGE_MAX_ID: usize = 9;
#[derive(Debug, Copy, Clone, IntoPrimitive, PartialEq, Eq)]
#[repr(u32)]
pub enum MacroStage {
Connect = 0,
Helo = 1,
MailFrom = 2,
RcptTo = 3,
Data = 4,
EndOfBody = 5,
EndOfHeaders = 6,
Header = 7,
Body = 8,
Unknown = MACRO_STAGE_MAX_ID as u32,
}
impl From<usize> for MacroStage {
fn from(value: usize) -> Self {
match value {
0 => Self::Connect, 1 => Self::Helo, 2 => Self::MailFrom, 3 => Self::RcptTo, 4 => Self::Data, 5 => Self::EndOfBody, 6 => Self::EndOfHeaders, 7 => Self::Header,
8 => Self::Body,
_ => Self::Unknown,
}
}
}
impl From<u32> for MacroStage {
fn from(value: u32) -> Self {
Self::from(value as usize)
}
}
impl MacroStage {
const CODE_SIZE: usize = 4;
fn as_usize(self) -> usize {
let self_u32: u32 = self.into();
self_u32 as usize
}
}