luff 0.2.1

Print files with formatting
Documentation
# Config Module

**Purpose**: Translate CLI arguments (and config files / env vars) into immutable runtime configuration with compiled pattern matchers.

## File Responsibilities

### patterns.rs

- **IgnorePatterns struct**: Container for all filtering rules
- **Glob integration**: Embeds `GlobMatcher` for complex pattern matching
- **Default patterns**: Hard-coded ignore rules (binary extensions, build artifacts)
- **should*ignore*\* methods**: Fast filtering predicates

### file.rs

- **ConfigFile struct**: YAML-backed configuration with serde support
- **CliOverride struct**: CLI-provided overrides (all optional)
- **ValidatedConfig struct**: Immutable, validated configuration
- **load_config_file()**: Secure file loading with TOCTOU mitigation

### mod.rs

- **Config struct**: Top-level runtime configuration wrapping `ValidatedConfig`
- **Config builder**: Converts Args → Config via layered Figment pipeline
- **EnvOverrides**: Custom Figment provider for `LUFF_*` env vars
- **Getters**: Read-only access via const fn where possible
- **printer_options()**: Constructs PrinterOptions for output formatters

## Critical Types

```rust
Config {
    validated: ValidatedConfig,  // All settings from config pipeline
    root: PathBuf,               // Base directory (absolute, from git or cwd)
    skip_patterns: SkipPatterns, // Whether pattern filtering is active
}

ValidatedConfig {
    include_dotfiles: bool,      // Show hidden files/dirs
    respect_gitignore: bool,     // Honor .gitignore patterns
    format: OutputFormat,        // Markdown | Tree
    max_depth: usize,            // 0 = unlimited, max 100
    max_files: usize,            // 1–10_000_000
    max_clipboard_mb: usize,     // 0–1000
    patterns: IgnorePatterns,    // Compiled ignore rules
}

IgnorePatterns {
    extensions: HashSet<String>,   // Binary/non-text extensions
    directories: HashSet<String>,  // Build artifacts, caches
    files: HashSet<String>,        // Specific filenames (e.g., lock files)
    globs: GlobMatcher,            // Compiled glob patterns (e.g., src/**/*.test.rs)
}
```

## Precedence Order (highest → lowest)

1. CLI arguments (`CliOverride`)
2. Environment variables (`LUFF_*`, via `EnvOverrides`)
3. Config file (YAML, via `load_config_file`)
4. Built-in defaults (`ConfigFile::default`)

### Environment Variable Coverage

The following `LUFF_*` variables are supported:

- `LUFF_INCLUDE_DOTFILES` (bool)
- `LUFF_RESPECT_GITIGNORE` (bool)
- `LUFF_FORMAT` (string: "markdown" or "tree")
- `LUFF_MAX_DEPTH` (u64)
- `LUFF_MAX_FILES` (u64)
- `LUFF_MAX_CLIPBOARD_MB` (u64)

Pattern lists (`ignore_extensions`, `ignore_directories`, `ignore_files`,
`ignore_globs`) are intentionally **not** exposed as env vars because
comma-separated list parsing in env vars is error-prone and ambiguous.
Use a config file for custom patterns.

## Critical Methods

```rust
Config::from_args(args) -> Result<Config>
    // Primary constructor, may call git::find_repository_root()

Config::from_args_with_env(args, env) -> Result<Config>
    // Testable variant with injected EnvProvider

Config::root() -> &Path
Config::patterns() -> &IgnorePatterns
    // Zero-cost getters (return references)

IgnorePatterns::should_ignore_extension(ext) -> bool
    // O(1) HashSet lookup (case-insensitive)

IgnorePatterns::should_ignore_glob(path) -> bool
    // O(m) GlobSet match

Config::printer_options() -> PrinterOptions
    // Clones root + format + patterns for printer configuration
```

## Key Invariants

- Config is created once, shared via &Config (no Arc needed for single-threaded CLI)
- root is always absolute (validated during from_args)
- ValidatedConfig has private fields; only constructible through ConfigFile::validate()
- IgnorePatterns use lowercase strings for case-insensitive extension matching
- Glob patterns are compiled at config time, failing loudly if invalid

## Performance Notes

- HashSet lookups are O(1) average case
- Glob matching uses optimized DFAs from `globset`
- Patterns compiled once at Config creation
- Clone is cheap (small fields, HashSets/GlobSet are Arc-based internally)
- `max_clipboard_bytes()` is computed on access (no cached field to drift)