1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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 {}