pub(crate) mod minimal;
pub(crate) mod pipe;
pub(crate) mod field;
pub(crate) mod narrate;
#[cfg(test)]
pub(crate) mod test_support;
use std::io;
use zond_engine::export::Redaction;
use zond_engine::{Host, ScanReport};
use crate::diagnostics::Verbosity;
use crate::settings::Presentation;
use crate::target::{ScanTargets, Targets};
#[derive(Clone, Copy)]
pub(crate) enum Phase<'a> {
Discovery {
targets: &'a Targets,
},
PortScan {
targets: &'a ScanTargets,
},
}
pub(crate) trait Renderer {
fn started(&mut self, phase: Phase<'_>, redaction: Redaction) -> io::Result<()>;
fn host_found(&mut self, host: &Host) -> io::Result<()>;
fn interrupted(&mut self) -> io::Result<()>;
fn finished(&mut self, report: &ScanReport) -> io::Result<()>;
}
#[derive(Debug, thiserror::Error)]
#[error(
"the '{presentation}' presentation is not built yet. Built so far: {}.",
Presentation::ALL
.iter()
.filter(|mode| mode.is_available())
.map(|mode| mode.as_str())
.collect::<Vec<_>>()
.join(", ")
)]
pub(crate) struct Unavailable {
pub presentation: Presentation,
}
pub(crate) fn renderer(
presentation: Presentation,
verbosity: Verbosity,
) -> Result<Box<dyn Renderer>, Unavailable> {
match presentation {
Presentation::Pipe => Ok(Box::new(pipe::PipeRenderer::to_terminal(verbosity))),
Presentation::Minimal => Ok(Box::new(minimal::MinimalRenderer::to_terminal(verbosity))),
Presentation::Standard | Presentation::Fancy => Err(Unavailable { presentation }),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_built_modes_produce_a_renderer() {
assert!(renderer(Presentation::Pipe, Verbosity::default()).is_ok());
assert!(renderer(Presentation::Minimal, Verbosity::default()).is_ok());
}
#[test]
fn a_mode_that_is_not_built_is_refused_rather_than_substituted() {
for mode in [Presentation::Standard, Presentation::Fancy] {
let refused = renderer(mode, Verbosity::default());
let Err(unavailable) = refused else {
panic!("{mode} is not built and must not silently become another mode");
};
assert_eq!(unavailable.presentation, mode);
assert!(unavailable.to_string().contains("minimal"));
}
}
#[test]
fn availability_agrees_with_what_can_be_built() {
for mode in Presentation::ALL {
assert_eq!(
mode.is_available(),
renderer(mode, Verbosity::default()).is_ok(),
"{mode} disagrees with itself"
);
}
}
}