use std::{error::Error, fmt, sync::mpsc::Sender};
use log::{debug, trace};
use crate::{binary::Bit, cli::progress::ProgressStatus, context::Context};
#[derive(Debug, Clone)]
pub enum EncoderResult {
Success,
NoDataLeft,
}
pub trait Capacity {
fn bitrate(&self) -> usize;
}
pub trait Encoder<E>: Capacity
where
E: Context,
{
fn partial_encode(
&self,
context: &mut E,
data: &mut dyn Iterator<Item = Bit>,
) -> Result<EncoderResult, Box<dyn Error>>;
fn encode(
&self,
context: &mut E,
data: &mut dyn Iterator<Item = Bit>,
progress_channel: Option<&Sender<ProgressStatus>>,
) -> Result<String, Box<dyn Error>> {
let mut stego_text = String::new();
let mut no_data_left = false;
while !no_data_left {
context.load_text()?;
trace!("Current line '{}'", context.get_current_text()?);
match self.partial_encode(context, data)? {
EncoderResult::Success => {
if let Some(tx) = progress_channel {
tx.send(ProgressStatus::Step(self.bitrate() as u64)).ok();
}
}
EncoderResult::NoDataLeft => {
debug!("No data left to encode, stopping");
no_data_left = true;
}
}
let line = context.get_current_text()?;
stego_text.push_str(&format!("{}\n", &line));
}
let mut appended_line_count = 0;
while let Ok(line) = context.load_text() {
appended_line_count += 1;
stego_text.push_str(&format!("{}\n", &line));
}
debug!("Appended the {} of left lines", appended_line_count);
if !no_data_left {
debug!("Capacity exceeded by {} bits", data.count());
Err(EncodingError::capacity_error().into())
} else {
Ok(stego_text)
}
}
}
#[derive(Debug, Clone)]
pub enum EncodingErrorKind {
CapacityTooLow,
NoWordsLeft,
}
#[derive(Debug, Clone)]
pub struct EncodingError {
kind: EncodingErrorKind,
}
impl EncodingError {
pub fn capacity_error() -> Self {
EncodingError {
kind: EncodingErrorKind::CapacityTooLow,
}
}
pub fn no_words_error() -> Self {
EncodingError {
kind: EncodingErrorKind::NoWordsLeft,
}
}
}
#[cfg(not(tarpaulin_include))]
impl fmt::Display for EncodingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
EncodingErrorKind::CapacityTooLow => write!(f, "Exceeded cover text capacity"),
EncodingErrorKind::NoWordsLeft => write!(
f,
"No extra words found in cover text when tried to encode a bit"
),
}
}
}
impl Error for EncodingError {}