Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Output

The output module combines a formatter and a writer to produce scan results in the requested format and destination.

Output

#![allow(unused)]
fn main() {
pub struct Output {
    writer: WriterImpl,
    formatter: FormatterImpl,
}
}

Constructor

#![allow(unused)]
fn main() {
pub fn new(
    output_format: OutputFormat,
    output_path: Option<PathBuf>,
) -> Result<Self, TowlOutputError>
}

Creates an output handler by selecting the appropriate formatter and writer.

Format-to-writer mapping:

FormatWriterOutput path
Table / TerminalStdoutWriterMust be None
JsonFileWriterRequired, must end in .json
CsvFileWriterRequired, must end in .csv
TomlFileWriterRequired, must end in .toml
MarkdownFileWriterRequired, must end in .md

save

#![allow(unused)]
fn main() {
pub async fn save(&self, todos: &[TodoComment]) -> Result<(), TowlOutputError>
}

Formats the TODOs and writes them to the destination. TODOs are grouped by type before formatting.

OutputFormat

#![allow(unused)]
fn main() {
pub enum OutputFormat {
    Table,
    Json,
    Csv,
    Toml,
    Markdown,
    Terminal,
}
}

Used as a CLI argument via clap::ValueEnum. Table and Terminal are treated identically.

Formatter Dispatch

Internally, FormatterImpl is an enum that dispatches to the correct formatter without dynamic dispatch:

#![allow(unused)]
fn main() {
pub(crate) enum FormatterImpl {
    Csv(CsvFormatter),
    Json(JsonFormatter),
    Markdown(MarkdownFormatter),
    Table(TableFormatter),
    Toml(TomlFormatter),
}
}

Each formatter implements the internal Formatter trait:

#![allow(unused)]
fn main() {
pub(crate) trait Formatter {
    fn format(
        &self,
        todos: &HashMap<&TodoType, Vec<&TodoComment>>,
        total_count: usize,
    ) -> Result<Vec<String>, FormatterError>;
}
}

Writer Dispatch

WriterImpl dispatches between stdout and file output:

#![allow(unused)]
fn main() {
pub(crate) enum WriterImpl {
    Stdout(StdoutWriter),
    File(FileWriter),
}
}

Each writer implements the internal Writer trait:

#![allow(unused)]
fn main() {
pub(crate) trait Writer {
    async fn write(&self, content: Vec<String>) -> Result<(), WriterError>;
}
}

FileWriter

Validates the output path on construction:

  • Rejects path traversal (.. components)
  • Resolves symlinks before writing

StdoutWriter

Writes each formatted line to stdout followed by a newline.

Errors

TowlOutputError

#![allow(unused)]
fn main() {
pub enum TowlOutputError {
    InvalidOutputPath(String),
    UnableToFormatTodos(FormatterError),
    UnableToWriteTodos(WriterError),
}
}

FormatterError

#![allow(unused)]
fn main() {
pub enum FormatterError {
    SerializationError(String),
    IntegerOverflow(usize),
}
}

WriterError

#![allow(unused)]
fn main() {
pub enum WriterError {
    IoError(std::io::Error),
    PathTraversal(PathBuf),
}
}