luff 0.2.1

Print files with formatting
Documentation
# Printer Module

**Purpose**: Output formatting abstraction - converts WalkerEntry to formatted stdout/string.

## File Responsibilities

### mod.rs

- **print_file()**: Dispatcher (OutputFormat → printer implementation)
- **PrinterOptions**: Config carrier (format + root + patterns)
- **Re-exports**: Public printer types

### markdown.rs

- **MarkdownPrinter::print()**: Stateless formatter, streams output to stdout
- **Streaming I/O**: Uses `BufReader` to process files in chunks (low memory footprint)
- **Binary Detection**: Checks first chunk for NUL bytes to skip binary files
- **UTF-8 Handling**: Streaming decoder handles split characters, replaces invalid sequences
- **Locked stdout**: Single lock per file for efficiency

### tree.rs

- **TreePrinter**: Stateful accumulator for batch mode
- **TreeNode building**: Constructs in-memory hierarchy from paths
- **Recursive printing**: Pretty-print with Unicode box-drawing
- **Streaming mode**: TreePrinter::print() for single entries

## Critical Types

```rust
PrinterOptions {
    format: OutputFormat,
    root: PathBuf,          // For calculating relative paths in output
    patterns: IgnorePatterns, // For filtering content (binary checks)
    skip_patterns: SkipPatterns, // Whether to apply filtering
}

MarkdownPrinter  // Zero-sized type (stateless)

TreePrinter {
    entries: Vec<PathBuf>  // For batch accumulation
}
```

## Critical Methods

```rust
print_file(entry, options) -> Result<()>
    // Dispatcher, delegates to MarkdownPrinter or TreePrinter

MarkdownPrinter::print(entry, root, patterns, skip) -> Result<()>
    // Streams: relative_path\n```ext\n[content]\n```\n
    // Checks patterns and content (NUL bytes) before full stream

TreePrinter::print(entry, root) -> Result<()>
    // Streaming: prints single entry with icon

TreePrinter::print_tree(&self, root) -> Result<()>
    // Batch: prints accumulated tree structure
```

## Output Formats

### Markdown (fenced code blocks)

```
relative/path/to/file.rs
```rs
fn main() {}
```
```

### Tree (directory hierarchy)

```
📁 src
├── 📄 main.rs
└── 📁 config
    └── 📄 mod.rs
```

## Key Invariants

- All printers write to stdout (no file handles passed in)
- stdout locked once per print_file() call (not per line write)
- Binary files filtered by extension AND content inspection (NUL check)
- Invalid UTF-8 is replaced with  (lossy conversion), not skipped
- MarkdownPrinter streams data (O(1) memory), does not load full file
- TreePrinter requires full traversal + in-memory tree for hierarchy

## Performance Notes

- **Streaming**: MarkdownPrinter uses 8KB buffer, constant memory usage regardless of file size
- **Locked stdout**: Eliminates repeated lock/unlock overhead
- **Binary detection**: Fast-path extension check, fallback to first-chunk NUL check
- **Tree printer**: Requires O(N) memory to build hierarchy

## Security Considerations

```rust
const MAX_FILE_SIZE: u64 = 100 * 1024 * 1024;  // Enforced before read
```

- Size check before open prevents processing massive files
- Streaming prevents OOM even for large allowed files (up to 100MB)
- No shell escape sequences in output (terminal-safe)
- Paths sanitized via Display trait

## Extension Points

- Syntax highlighting: Hook into markdown.rs, integrate syntect/bat
- Custom formats: Add variant to OutputFormat, implement new printer
- JSON output: Serialize WalkerEntry with file contents
- Pager integration: Detect TTY, pipe through less in print_file()