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

Processor

The processor replaces TODO comments in source files with GitHub issue links after issues are created.

Processor

#![allow(unused)]
fn main() {
pub struct Processor;
}

Stateless processor that operates on batches of (TodoComment, CreatedIssue) pairs. All methods are associated functions (no self).

replace_todos

#![allow(unused)]
fn main() {
pub async fn replace_todos(
    repo_root: &Path,
    replacements: &[(TodoComment, CreatedIssue)],
) -> ProcessorResult
}

Replaces TODO comments in source files with See: <issue_url> links.

Behaviour:

  1. Groups replacements by file path for efficient batch processing
  2. For each file, validates the path stays within repo_root
  3. Reads file content, replaces each TODO line, writes back atomically
  4. Returns a ProcessorResult with counts and per-file errors

Path safety:

  • Both the file path and repo root are canonicalised before comparison
  • Files outside the repo root are rejected with PathOutsideRoot
  • Issue URLs must start with https://github.com/ or are rejected

Replacement format:

The comment prefix (e.g., // , # , /* ) is preserved. The TODO text after the prefix is replaced:

// TODO: Implement caching    -->    // See: https://github.com/owner/repo/issues/42
# FIXME: Handle timeout        -->    # See: https://github.com/owner/repo/issues/43

Atomic writes:

Files are written via a tempfile in the same directory, then atomically persisted. This prevents partial writes if the process is interrupted.

Empty input:

If replacements is empty, returns immediately with zero counts and no I/O.

ProcessorResult

#![allow(unused)]
fn main() {
pub struct ProcessorResult {
    pub files_modified: usize,
    pub todos_replaced: usize,
    pub errors: Vec<(PathBuf, TowlProcessorError)>,
}
}

Summary of a batch replacement operation. The errors field contains per-file errors that did not abort the overall operation -- other files continue processing.

Errors

#![allow(unused)]
fn main() {
pub enum TowlProcessorError {
    FileReadError(PathBuf, std::io::Error),
    FileWriteError(PathBuf, std::io::Error),
    LineOutOfBounds { path: PathBuf, line: usize, total_lines: usize },
    CommentPrefixNotFound { path: PathBuf, line: usize },
    PathOutsideRoot { path: PathBuf, root: PathBuf },
    InvalidIssueUrl { url: String },
}
}
VariantCause
FileReadErrorFailed to read source file
FileWriteErrorFailed to write modified file
LineOutOfBoundsTODO line number exceeds file length
CommentPrefixNotFoundColumn offset points past end of line
PathOutsideRootFile is outside the repository root
InvalidIssueUrlURL does not start with https://github.com/

Example

#![allow(unused)]
fn main() {
use towl::processor::Processor;

let replacements = vec![(todo, created_issue)];
let result = Processor::replace_todos(repo_root, &replacements).await;

println!("Modified {} files, replaced {} TODOs", result.files_modified, result.todos_replaced);

for (path, err) in &result.errors {
    eprintln!("Error in {}: {}", path.display(), err);
}
}