crafter/wire/
transform.rs1use super::record::{PacketRecord, TransformTrace};
4use super::Result;
5
6pub trait PacketTransform {
16 fn name(&self) -> &'static str;
18
19 fn transform(
21 &mut self,
22 record: PacketRecord,
23 emit: &mut dyn FnMut(PacketRecord) -> Result<()>,
24 ) -> Result<()>;
25
26 fn transform_to_output(&mut self, record: PacketRecord) -> Result<TransformOutput>
28 where
29 Self: Sized,
30 {
31 let mut output = TransformOutput::new();
32 self.transform(record, &mut |record| {
33 output.push(record);
34 Ok(())
35 })?;
36 Ok(output)
37 }
38}
39
40#[derive(Debug, Clone, Default)]
42pub struct TransformOutput {
43 records: Vec<PacketRecord>,
44}
45
46impl TransformOutput {
47 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn records(&self) -> &[PacketRecord] {
54 &self.records
55 }
56
57 pub fn len(&self) -> usize {
59 self.records.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.records.is_empty()
65 }
66
67 pub fn push(&mut self, record: PacketRecord) -> &mut Self {
69 self.records.push(record);
70 self
71 }
72
73 pub fn emit(&mut self, record: PacketRecord) -> Result<()> {
75 self.records.push(record);
76 Ok(())
77 }
78
79 pub fn clear(&mut self) -> &mut Self {
81 self.records.clear();
82 self
83 }
84
85 pub fn into_records(self) -> Vec<PacketRecord> {
87 self.records
88 }
89}
90
91#[derive(Debug, Clone, Default)]
93pub struct PassThroughTransform {
94 input_count: usize,
95 emitted_count: usize,
96}
97
98impl PassThroughTransform {
99 pub fn new() -> Self {
101 Self::default()
102 }
103
104 pub const fn input_count(&self) -> usize {
106 self.input_count
107 }
108
109 pub const fn emitted_count(&self) -> usize {
111 self.emitted_count
112 }
113}
114
115impl PacketTransform for PassThroughTransform {
116 fn name(&self) -> &'static str {
117 "pass-through"
118 }
119
120 fn transform(
121 &mut self,
122 record: PacketRecord,
123 emit: &mut dyn FnMut(PacketRecord) -> Result<()>,
124 ) -> Result<()> {
125 self.input_count += 1;
126 emit(record)?;
127 self.emitted_count += 1;
128 Ok(())
129 }
130}
131
132#[derive(Debug, Clone, Default)]
134pub struct DropAllTransform {
135 dropped_count: usize,
136}
137
138impl DropAllTransform {
139 pub fn new() -> Self {
141 Self::default()
142 }
143
144 pub const fn dropped_count(&self) -> usize {
146 self.dropped_count
147 }
148}
149
150impl PacketTransform for DropAllTransform {
151 fn name(&self) -> &'static str {
152 "drop-all"
153 }
154
155 fn transform(
156 &mut self,
157 _record: PacketRecord,
158 _emit: &mut dyn FnMut(PacketRecord) -> Result<()>,
159 ) -> Result<()> {
160 self.dropped_count += 1;
161 Ok(())
162 }
163}
164
165#[derive(Debug, Clone, Default)]
167pub struct DuplicateTransform {
168 input_count: usize,
169 emitted_count: usize,
170}
171
172impl DuplicateTransform {
173 pub fn new() -> Self {
175 Self::default()
176 }
177
178 pub const fn input_count(&self) -> usize {
180 self.input_count
181 }
182
183 pub const fn emitted_count(&self) -> usize {
185 self.emitted_count
186 }
187}
188
189impl PacketTransform for DuplicateTransform {
190 fn name(&self) -> &'static str {
191 "duplicate"
192 }
193
194 fn transform(
195 &mut self,
196 record: PacketRecord,
197 emit: &mut dyn FnMut(PacketRecord) -> Result<()>,
198 ) -> Result<()> {
199 self.input_count += 1;
200 emit(record.clone())?;
201 self.emitted_count += 1;
202 emit(record)?;
203 self.emitted_count += 1;
204 Ok(())
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct TraceAppendTransform {
211 name: &'static str,
212 note: Option<String>,
213 input_count: usize,
214 emitted_count: usize,
215}
216
217impl TraceAppendTransform {
218 pub const fn new(name: &'static str) -> Self {
220 Self {
221 name,
222 note: None,
223 input_count: 0,
224 emitted_count: 0,
225 }
226 }
227
228 pub const fn trace_append() -> Self {
230 Self::new("trace-append")
231 }
232
233 pub fn with_note(mut self, note: impl Into<String>) -> Self {
235 self.note = Some(note.into());
236 self
237 }
238
239 pub const fn input_count(&self) -> usize {
241 self.input_count
242 }
243
244 pub const fn emitted_count(&self) -> usize {
246 self.emitted_count
247 }
248}
249
250impl Default for TraceAppendTransform {
251 fn default() -> Self {
252 Self::trace_append()
253 }
254}
255
256impl PacketTransform for TraceAppendTransform {
257 fn name(&self) -> &'static str {
258 self.name
259 }
260
261 fn transform(
262 &mut self,
263 mut record: PacketRecord,
264 emit: &mut dyn FnMut(PacketRecord) -> Result<()>,
265 ) -> Result<()> {
266 self.input_count += 1;
267
268 let mut trace = TransformTrace::new(self.name());
269 if let Some(note) = &self.note {
270 trace = trace.with_note(note.clone());
271 }
272 record.metadata_mut().push_transform_trace(trace);
273
274 emit(record)?;
275 self.emitted_count += 1;
276 Ok(())
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::super::record::{BackendKind, PacketOrigin};
283 use super::super::WireError;
284 use super::*;
285 use crate::Raw;
286
287 fn record(payload: &'static str) -> PacketRecord {
288 PacketRecord::new(Raw::from(payload))
289 .with_origin(PacketOrigin::Generated)
290 .with_backend(BackendKind::Memory)
291 .with_interface("lo")
292 }
293
294 #[test]
295 fn transform_output_buffers_records_in_order() {
296 let mut output = TransformOutput::new();
297 assert!(output.is_empty());
298
299 output.push(record("one"));
300 output.emit(record("two")).unwrap();
301
302 assert_eq!(output.len(), 2);
303 assert_eq!(output.records()[0].packet().summary(), "Raw(len=3)");
304 assert_eq!(output.records()[1].packet().summary(), "Raw(len=3)");
305
306 output.clear();
307 assert!(output.is_empty());
308 }
309
310 #[test]
311 fn pass_through_emits_one_record_unchanged() {
312 let input = record("payload");
313 let mut transform = PassThroughTransform::new();
314
315 let output = transform.transform_to_output(input).unwrap();
316
317 assert_eq!(transform.name(), "pass-through");
318 assert_eq!(transform.input_count(), 1);
319 assert_eq!(transform.emitted_count(), 1);
320 assert_eq!(output.len(), 1);
321 assert_eq!(output.records()[0].packet().summary(), "Raw(len=7)");
322 assert_eq!(
323 output.records()[0].metadata().origin(),
324 PacketOrigin::Generated
325 );
326 assert_eq!(
327 output.records()[0].metadata().backend(),
328 &BackendKind::Memory
329 );
330 assert_eq!(output.records()[0].metadata().interface(), Some("lo"));
331 }
332
333 #[test]
334 fn drop_all_emits_zero_records() {
335 let mut transform = DropAllTransform::new();
336
337 let output = transform.transform_to_output(record("payload")).unwrap();
338
339 assert_eq!(transform.name(), "drop-all");
340 assert_eq!(transform.dropped_count(), 1);
341 assert!(output.is_empty());
342 }
343
344 #[test]
345 fn duplicate_emits_two_records_per_input() {
346 let mut transform = DuplicateTransform::new();
347
348 let output = transform.transform_to_output(record("payload")).unwrap();
349
350 assert_eq!(transform.name(), "duplicate");
351 assert_eq!(transform.input_count(), 1);
352 assert_eq!(transform.emitted_count(), 2);
353 assert_eq!(output.len(), 2);
354 assert_eq!(output.records()[0].packet().summary(), "Raw(len=7)");
355 assert_eq!(output.records()[1].packet().summary(), "Raw(len=7)");
356 assert_eq!(
357 output.records()[0].metadata().origin(),
358 PacketOrigin::Generated
359 );
360 assert_eq!(
361 output.records()[1].metadata().backend(),
362 &BackendKind::Memory
363 );
364 }
365
366 #[test]
367 fn trace_append_adds_transform_history() {
368 let mut transform = TraceAppendTransform::new("decode-ip").with_note("decoded");
369
370 let output = transform.transform_to_output(record("payload")).unwrap();
371
372 assert_eq!(transform.name(), "decode-ip");
373 assert_eq!(transform.input_count(), 1);
374 assert_eq!(transform.emitted_count(), 1);
375 assert_eq!(output.len(), 1);
376 let traces = output.records()[0].metadata().transforms();
377 assert_eq!(traces.len(), 1);
378 assert_eq!(traces[0].name(), "decode-ip");
379 assert_eq!(traces[0].note(), Some("decoded"));
380 assert_eq!(output.records()[0].packet().summary(), "Raw(len=7)");
381 }
382
383 #[test]
384 fn packet_transform_is_object_safe() {
385 let mut transform: Box<dyn PacketTransform> = Box::new(PassThroughTransform::new());
386 let mut output = TransformOutput::new();
387
388 transform
389 .transform(record("payload"), &mut |record| output.emit(record))
390 .unwrap();
391
392 assert_eq!(transform.name(), "pass-through");
393 assert_eq!(output.len(), 1);
394 assert_eq!(output.records()[0].packet().summary(), "Raw(len=7)");
395 }
396
397 #[test]
398 fn transform_propagates_emitter_errors() {
399 let mut transform = DuplicateTransform::new();
400
401 let err = transform
402 .transform(record("payload"), &mut |_record| {
403 Err(WireError::transform("collector", "closed"))
404 })
405 .unwrap_err();
406
407 assert_eq!(err.to_string(), "wire transform 'collector' failed: closed");
408 assert_eq!(transform.input_count(), 1);
409 assert_eq!(transform.emitted_count(), 0);
410 }
411}