luff 0.2.1

Print files with formatting
Documentation
# Walker Module

**Purpose**: Unified file iteration interface abstracting directory walking vs explicit file lists.

## File Responsibilities

### mod.rs

- **Walker enum**: Zero-cost wrapper over DirectoryWalker | FileListWalker
- **WalkerEntry**: Standard output (absolute path + relative path for display)
- **Constructors**: from_dir(), from_file_list()
- **Iterator impl**: Delegates to inner walker variant (no overhead)

### dir.rs

- **DirectoryWalker**: Wraps `ignore::WalkBuilder` with per-entry validation
- **Streaming semantics**: Files discovered lazily during iteration
- **Snapshot Isolation**: Uses `ignore` crate's directory snapshot mechanism
- **Syscall Optimization**: Uses cached `entry.metadata()` to avoid redundant `stat` calls
- **Filter chain**: Dotfiles → gitignore → **globs** → patterns → file type
- **Output protection**: Per-entry detection of shell redirection files

### file_list.rs

- **FileListWalker**: Validates and iterates explicit file list
- **Security validation**: Canonicalization, TOCTOU protection, root containment
- **Error filtering**: Invalid files logged to stderr, don't stop iteration
- **Zero-copy iteration**: `IntoIter<PathBuf>` moves values without cloning

## Critical Types

```rust
WalkerEntry {
    path: PathBuf,          // Absolute, canonical path
    relative_path: PathBuf, // Relative to config.root() for display
    is_dir: bool,
}

Walker = Directory(Box<DirectoryWalker>) | FileList(Box<FileListWalker>)
    // Box needed for size optimization (DirectoryWalker contains state machine)
```

## Critical Methods

```rust
Walker::from_dir(config) -> Result<Walker>
    // Creates DirectoryWalker with lazy iteration semantics

Walker::from_file_list(files, config) -> Result<Walker>
    // Validates each file, filters invalid paths, returns validated iterator

DirectoryWalker::new(config) -> Result<Self>
    // Builds WalkBuilder, configures filters, pre-computes stdout identity
    // O(1) construction - no filesystem traversal occurs

FileListWalker::validate_file(path, root) -> Result<PathBuf>
    // Security: canonicalize, check is_file(), verify within root
```

## Streaming Semantics (DirectoryWalker)

**Design Rationale**: Files are discovered during iteration rather than pre-collected to:

- **Minimize startup latency**: Begin processing immediately
- **Bounded memory**: O(1) peak regardless of directory size
- **Snapshot isolation**: `ignore` crate takes directory snapshot at Walk construction
- **Graceful degradation**: Single file errors don't abort entire walk

**Filter Chain Order:**
1. `ignore` crate filters (dotfiles, .gitignore)
2. **Glob patterns** (user-provided via `--ignore` or config)
3. Directory patterns (e.g., `.git`)
4. File patterns (e.g., extensions)

**Shell Redirection Protection:**

```rust
// Skip files that match stdout inode OR are empty + recently created
// Catches: `luff > output.txt` (shell creates before process starts)
```

## Security (FileListWalker)

```rust
validate_file(path, root) checks:
1. path.exists() && path.metadata().is_file() (fast-path)
2. path.canonicalize() (resolve symlinks to absolute form)
3. canonical.metadata().is_file() (TOCTOU: verify after canonicalize)
4. is_canonical_within_root(canonical, canonical_root) (prevent traversal)
```

## Data Flow

```
Config → Walker construction → Lazy iteration
                                                                       Iterator yields WalkerItem (Entry | Error)
                                                                       Printer::print_file() or buffer_entries_with_limit()
```

## Key Invariants

- WalkerEntry.path is always absolute and canonical
- relative_path is always relative to config.root()
- Iterator never panics (errors yielded as WalkerItem::Error)
- DirectoryWalker uses ignore's snapshot isolation for TOCTOU safety
- FileListWalker validates all paths before iteration begins
- FileListWalker yields only files (is_dir is always false)

## Performance Characteristics

- **DirectoryWalker**: O(1) memory, first result available immediately
- **FileListWalker**: O(n) memory for validated path list (n = number of valid files)
- **Enum dispatch**: Zero-cost (compiler optimizes to direct call)
- **No per-entry allocations**: Paths moved, not cloned
- **Syscall Deduplication**: DirectoryWalker reuses metadata from directory entry

## Extension Points

- Parallel walking: Use `ignore::WalkParallel` (requires `Arc<Config>` + channels)
- Progress bars: Use `size_hint()` with `indicatif` crate
- Async I/O: Create async Walker variant with `tokio::fs`
- Custom walkers: Add variant to `Walker` enum, implement `Iterator`