Skip to main content

pcap_processor/output/
formatter.rs

1use crate::cli::OutputFormat;
2use crate::error::{PcapError, Result};
3use serde::Serialize;
4
5#[derive(Serialize)]
6pub struct OutputData {
7    pub field: String,
8    pub total_unique: usize,
9    pub values: Vec<ValueEntry>,
10}
11
12#[derive(Serialize)]
13pub struct ValueEntry {
14    pub value: String,
15    pub count: usize,
16}
17
18pub fn format_output(data: &OutputData, format: OutputFormat, show_count: bool) -> Result<String> {
19    match format {
20        OutputFormat::Plain => format_plain(data, show_count),
21        OutputFormat::Json => format_json(data, show_count),
22        OutputFormat::Csv => format_csv(data, show_count),
23    }
24}
25
26fn format_plain(data: &OutputData, show_count: bool) -> Result<String> {
27    let mut output = String::new();
28
29    for entry in &data.values {
30        if show_count {
31            output.push_str(&format!("{}\t{}\n", entry.count, entry.value));
32        } else {
33            output.push_str(&entry.value);
34            output.push('\n');
35        }
36    }
37
38    Ok(output)
39}
40
41fn format_json(data: &OutputData, show_count: bool) -> Result<String> {
42    #[derive(Serialize)]
43    struct JsonOutput<'a> {
44        field: &'a str,
45        total_unique: usize,
46        values: Vec<JsonValue<'a>>,
47    }
48
49    #[derive(Serialize)]
50    struct JsonValue<'a> {
51        value: &'a str,
52        #[serde(skip_serializing_if = "Option::is_none")]
53        count: Option<usize>,
54    }
55
56    let output = JsonOutput {
57        field: &data.field,
58        total_unique: data.total_unique,
59        values: data
60            .values
61            .iter()
62            .map(|e| JsonValue {
63                value: &e.value,
64                count: if show_count { Some(e.count) } else { None },
65            })
66            .collect(),
67    };
68
69    serde_json::to_string_pretty(&output).map_err(|e| PcapError::OutputFormat(e.to_string()))
70}
71
72fn format_csv(data: &OutputData, show_count: bool) -> Result<String> {
73    let mut wtr = csv::Writer::from_writer(Vec::new());
74
75    // Write header
76    if show_count {
77        wtr.write_record(["value", "count"])
78            .map_err(|e| PcapError::OutputFormat(e.to_string()))?;
79    } else {
80        wtr.write_record(["value"])
81            .map_err(|e| PcapError::OutputFormat(e.to_string()))?;
82    }
83
84    // Write data
85    for entry in &data.values {
86        if show_count {
87            wtr.write_record([&entry.value, &entry.count.to_string()])
88                .map_err(|e| PcapError::OutputFormat(e.to_string()))?;
89        } else {
90            wtr.write_record([&entry.value])
91                .map_err(|e| PcapError::OutputFormat(e.to_string()))?;
92        }
93    }
94
95    let bytes = wtr
96        .into_inner()
97        .map_err(|e| PcapError::OutputFormat(e.to_string()))?;
98
99    String::from_utf8(bytes).map_err(|e| PcapError::OutputFormat(e.to_string()))
100}