use std::io::Write;
use crate::VerifiedFinding;
use super::{
impl_writer_backed, JsonReportCoverageGap, JsonlStreamHeader, JsonlStreamSummary, ReportError,
Reporter, ScanCompletionStatus, ScanReportMetadata, WriterBackedReporter,
JSON_REPORT_SCHEMA_MAJOR, JSON_REPORT_SCHEMA_MINOR,
};
pub(crate) struct JsonlReporter<W: Write + Send> {
writer: W,
}
impl<W: Write + Send> JsonlReporter<W> {
pub(crate) fn new(writer: W) -> Self {
Self { writer }
}
}
impl<W: Write + Send> Reporter for JsonlReporter<W> {
fn report(&mut self, finding: &VerifiedFinding) -> Result<(), ReportError> {
serde_json::to_writer(&mut self.writer, finding)?;
writeln!(self.writer)?;
Ok(())
}
fn finish(&mut self) -> Result<(), ReportError> {
self.flush_writer()
}
}
impl_writer_backed!(JsonlReporter);
pub(crate) struct JsonlEnvelopeReporter<W: Write + Send> {
writer: W,
finding_count: usize,
scan_status: ScanCompletionStatus,
coverage_gap_summary: Vec<(String, usize)>,
}
impl<W: Write + Send> JsonlEnvelopeReporter<W> {
pub(crate) fn new(
mut writer: W,
metadata: Option<&ScanReportMetadata>,
coverage_gap_summary: &[(String, usize)],
) -> Result<Self, ReportError> {
serde_json::to_writer(&mut writer, &JsonlStreamHeader::new(metadata))?;
writeln!(writer)?;
Ok(Self {
writer,
finding_count: 0,
scan_status: ScanCompletionStatus::resolve(
metadata.map(|value| value.scan_status),
!coverage_gap_summary.is_empty(),
),
coverage_gap_summary: coverage_gap_summary.to_vec(),
})
}
}
impl<W: Write + Send> Reporter for JsonlEnvelopeReporter<W> {
fn report(&mut self, finding: &VerifiedFinding) -> Result<(), ReportError> {
serde_json::to_writer(&mut self.writer, finding)?;
writeln!(self.writer)?;
self.finding_count += 1;
Ok(())
}
fn finish(&mut self) -> Result<(), ReportError> {
serde_json::to_writer(
&mut self.writer,
&JsonlStreamSummary::complete_with_status(
self.finding_count,
self.scan_status,
&self.coverage_gap_summary,
),
)?;
writeln!(self.writer)?;
self.flush_writer()
}
}
impl_writer_backed!(JsonlEnvelopeReporter);
pub(crate) struct JsonArrayReporter<W: Write + Send> {
writer: W,
first: bool,
}
impl<W: Write + Send> JsonArrayReporter<W> {
pub(crate) fn new(mut writer: W) -> Result<Self, ReportError> {
write!(writer, "[")?;
Ok(Self {
writer,
first: true,
})
}
}
impl<W: Write + Send> Reporter for JsonArrayReporter<W> {
fn report(&mut self, finding: &VerifiedFinding) -> Result<(), ReportError> {
if !self.first {
write!(self.writer, ",")?;
}
serde_json::to_writer(&mut self.writer, finding)?;
self.first = false;
Ok(())
}
fn finish(&mut self) -> Result<(), ReportError> {
write!(self.writer, "]")?;
self.flush_writer()
}
}
impl_writer_backed!(JsonArrayReporter);
pub(crate) struct JsonEnvelopeReporter<W: Write + Send> {
writer: W,
first: bool,
}
impl<W: Write + Send> JsonEnvelopeReporter<W> {
pub(crate) fn new(
mut writer: W,
metadata: Option<&ScanReportMetadata>,
coverage_gap_summary: &[(String, usize)],
) -> Result<Self, ReportError> {
write!(
writer,
"{{\"schema_version\":{{\"major\":{},\"minor\":{}}}",
JSON_REPORT_SCHEMA_MAJOR, JSON_REPORT_SCHEMA_MINOR
)?;
let scan_status = ScanCompletionStatus::resolve(
metadata.map(|value| value.scan_status),
!coverage_gap_summary.is_empty(),
);
write!(writer, ",\"scan_status\":")?;
serde_json::to_writer(&mut writer, &scan_status)?;
if let Some(metadata) = metadata {
write!(writer, ",\"metadata\":")?;
serde_json::to_writer(&mut writer, metadata)?;
}
write!(writer, ",\"coverage_gap_summary\":[")?;
for (index, (reason, count)) in coverage_gap_summary.iter().enumerate() {
if index > 0 {
write!(writer, ",")?;
}
serde_json::to_writer(
&mut writer,
&JsonReportCoverageGap {
reason: reason.clone(),
count: *count,
},
)?;
}
write!(writer, "]")?;
write!(writer, ",\"findings\":[")?;
Ok(Self {
writer,
first: true,
})
}
}
impl<W: Write + Send> Reporter for JsonEnvelopeReporter<W> {
fn report(&mut self, finding: &VerifiedFinding) -> Result<(), ReportError> {
if !self.first {
write!(self.writer, ",")?;
}
serde_json::to_writer(&mut self.writer, finding)?;
self.first = false;
Ok(())
}
fn finish(&mut self) -> Result<(), ReportError> {
write!(self.writer, "]}}")?;
self.flush_writer()
}
}
impl_writer_backed!(JsonEnvelopeReporter);