ptero/method/complex/
extended_line.rs1use crate::{
12 context::{PivotByLineContext, PivotByRawLineContext},
13 impl_complex_decoder, impl_complex_encoder,
14 method::{line_extend, random_whitespace, trailing_whitespace, Method},
15};
16
17type ExtendedLineSubmethod = Box<dyn Method<PivotByLineContext, PivotByRawLineContext>>;
18
19pub struct ExtendedLineMethod {
22 methods: Vec<ExtendedLineSubmethod>,
23}
24
25impl Default for ExtendedLineMethod {
26 fn default() -> Self {
27 ExtendedLineMethodBuilder::new().build()
28 }
29}
30
31#[derive(Debug, PartialEq)]
32pub enum ExtendedLineMethodVariant {
33 Variant1,
34 Variant2,
35 Variant3,
36}
37
38pub struct ExtendedLineMethodBuilder {
39 variant: ExtendedLineMethodVariant,
40}
41
42impl ExtendedLineMethodBuilder {
43 pub fn new() -> Self {
44 ExtendedLineMethodBuilder {
45 variant: ExtendedLineMethodVariant::Variant1,
46 }
47 }
48
49 pub fn variant(mut self, variant: ExtendedLineMethodVariant) -> Self {
50 self.variant = variant;
51 self
52 }
53
54 fn select_methods(&self) -> Vec<ExtendedLineSubmethod> {
55 let indices = match self.variant {
56 ExtendedLineMethodVariant::Variant1 => &[0, 1, 2],
57 ExtendedLineMethodVariant::Variant2 => &[1, 0, 2],
58 ExtendedLineMethodVariant::Variant3 => &[1, 2, 0],
59 };
60
61 indices
62 .iter()
63 .map(|i| {
64 let method: ExtendedLineSubmethod = match i {
65 0 => Box::new(random_whitespace::RandomWhitespaceMethod::default()),
66 1 => Box::new(line_extend::LineExtendMethod::default()),
67 _ => Box::new(trailing_whitespace::TrailingWhitespaceMethod::default()),
68 };
69 method
70 })
71 .collect()
72 }
73
74 pub fn build(&self) -> ExtendedLineMethod {
75 ExtendedLineMethod {
76 methods: self.select_methods(),
77 }
78 }
79}
80
81impl_complex_encoder!(ExtendedLineMethod, PivotByLineContext);
82impl_complex_decoder!(ExtendedLineMethod, PivotByRawLineContext);
83
84impl Method<PivotByLineContext, PivotByRawLineContext> for ExtendedLineMethod {
85 fn method_name(&self) -> String {
86 format!(
87 "ExtendedLineMethod({},{},{})",
88 self.methods[0].method_name(),
89 self.methods[1].method_name(),
90 self.methods[2].method_name(),
91 )
92 }
93}
94
95#[allow(unused_imports)]
96mod test {
97 use std::error::Error;
98
99 use crate::{
100 binary::BitIterator,
101 context::{PivotByLineContext, PivotByRawLineContext},
102 decoder::Decoder,
103 encoder::Encoder,
104 method::Method,
105 };
106
107 use super::{ExtendedLineMethod, ExtendedLineMethodBuilder, ExtendedLineMethodVariant};
108
109 #[test]
110 fn encodes_text_data() -> Result<(), Box<dyn Error>> {
111 let cover_input = "a b c".repeat(5);
112 let data_input = "a";
113 let pivot: usize = 4;
114
115 let mut data_iterator = BitIterator::new(&data_input.as_bytes());
116 let method = ExtendedLineMethod::default();
117 let mut context = PivotByLineContext::new(&cover_input, pivot);
118 let stego_text = method.encode(&mut context, &mut data_iterator, None)?;
119
120 assert_eq!(&stego_text, "a b ca \nb ca\nb ca b\nca b\nc \n");
121 Ok(())
122 }
123
124 #[test]
125 fn encodes_binary_data() -> Result<(), Box<dyn Error>> {
126 let cover_input = "a b c ".repeat(5);
127 let data_input: Vec<u8> = vec![0b11111111];
128 let pivot: usize = 3;
129
130 let mut data_iterator = BitIterator::new(&data_input);
131 let method = ExtendedLineMethod::default();
132 let mut context = PivotByLineContext::new(&cover_input, pivot);
133 let stego_text = method.encode(&mut context, &mut data_iterator, None)?;
134
135 assert_eq!(&stego_text, "a b c \na b c \na b c\na b\nc a\nb c \n");
136 Ok(())
137 }
138
139 #[test]
140 fn decodes_binary_data() -> Result<(), Box<dyn Error>> {
141 let stego_text = "a bc\na bcd\na b d \n";
142 let pivot: usize = 4;
143
144 let method = ExtendedLineMethod::default();
145 let mut context = PivotByRawLineContext::new(&stego_text, pivot);
146 let secret_data = method.decode(&mut context, None)?;
147
148 assert_eq!(&secret_data, &[0b100_010_11, 0b100_000_00]);
149 Ok(())
150 }
151
152 #[test]
153 fn decodes_zeroes_if_no_data_encoded() -> Result<(), Box<dyn Error>> {
154 let stego_text = "a\n".repeat(5);
155 let pivot: usize = 4;
156
157 let method = ExtendedLineMethod::default();
158 let mut context = PivotByRawLineContext::new(&stego_text, pivot);
159 let secret_data = method.decode(&mut context, None)?;
160
161 assert_eq!(&secret_data, &[0, 0]);
162 Ok(())
163 }
164
165 #[test]
166 fn default_method_is_variant_1() -> Result<(), Box<dyn Error>> {
167 assert_eq!(
168 ExtendedLineMethod::default().method_name(),
169 "ExtendedLineMethod(RandomWhitespaceMethod,LineExtendMethod,TrailingWhitespaceMethod)"
170 );
171 Ok(())
172 }
173
174 #[test]
175 fn builder_properly_constructs_variants() -> Result<(), Box<dyn Error>> {
176 assert_eq!(
177 ExtendedLineMethodBuilder::new()
178 .variant(ExtendedLineMethodVariant::Variant1)
179 .build()
180 .method_name(),
181 "ExtendedLineMethod(RandomWhitespaceMethod,LineExtendMethod,TrailingWhitespaceMethod)"
182 );
183 assert_eq!(
184 ExtendedLineMethodBuilder::new()
185 .variant(ExtendedLineMethodVariant::Variant2)
186 .build()
187 .method_name(),
188 "ExtendedLineMethod(LineExtendMethod,RandomWhitespaceMethod,TrailingWhitespaceMethod)"
189 );
190 assert_eq!(
191 ExtendedLineMethodBuilder::new()
192 .variant(ExtendedLineMethodVariant::Variant3)
193 .build()
194 .method_name(),
195 "ExtendedLineMethod(LineExtendMethod,TrailingWhitespaceMethod,RandomWhitespaceMethod)"
196 );
197 Ok(())
198 }
199}