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
Terminal (alias table)StdoutWriterMust 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 and sorted (by type priority, then file path and line number) before formatting, so identical scans produce byte-identical output.

OutputFormat

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

Used as a CLI argument via clap::ValueEnum. Terminal also accepts the value table as an alias.

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),
}
}