fast-fs 0.2.0

High-speed async file system traversal library with batteries-included file browser component
Documentation
<!-- <FILE>crates/fast-fs/README.md</FILE> - <DESC>fast-fs library documentation</DESC> -->
<!-- <VERS>VERSION: 0.9.0</VERS> -->
<!-- <WCTX>Preparing for crates.io release</WCTX> -->
<!-- <CLOG>Added badges, OFPF version note, fixed relative links for crates.io</CLOG> -->

# fast-fs

[![Crates.io](https://img.shields.io/crates/v/fast-fs.svg)](https://crates.io/crates/fast-fs)
[![Documentation](https://docs.rs/fast-fs/badge.svg)](https://docs.rs/fast-fs)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

High-speed async file system traversal and navigation library for Rust.

## Overview

fast-fs provides two main capabilities:

1. **Directory Reading Functions** - Simple async functions for traversing directories
2. **Navigation Module (`nav`)** - A batteries-included file browser state machine for building file pickers, save dialogs, and project explorers

The library is framework-agnostic: you handle rendering and input capture, fast-fs manages all browser state, navigation logic, and file operations.

## Features

- **Async Directory Reading** - Non-blocking traversal using tokio
- **Streaming API** - Memory-efficient traversal with backpressure support
- **Gitignore Support** - Built-in .gitignore and .ignore pattern matching
- **Advanced Filtering** - Depth limits, extensions, glob patterns, hidden files
- **File Browser Component** - Complete state machine with vim-style keybindings
- **Multi-Selection** - Range selection with Shift+Arrow, toggle with Space
- **Clipboard Model** - Cut/copy intent tracking with conflict detection
- **Navigation History** - Back/forward with H/L keys
- **Parent Directory Entry** - Optional ".." entry at top of file list
- **File Categorization** - Classify files as Code, Image, Video, Document, etc.
- **UI Integration Helpers** - `scroll_offset()`, `visible_range()` for viewport management

## Installation

```toml
[dependencies]
fast-fs = "0.2"
tokio = { version = "1.48", features = ["rt", "fs"] }
```

## Quick Start

### Directory Reading

```rust
use fast_fs::{read_dir, read_dir_recursive, TraversalOptions};

#[tokio::main]
async fn main() -> Result<(), fast_fs::Error> {
    // Read a single directory
    let files = read_dir("/path/to/dir").await?;

    // Recursive with options
    let options = TraversalOptions::default()
        .with_max_depth(3)
        .with_extensions(&["rs", "toml"]);

    let all_files = read_dir_recursive("/project", options).await?;

    for file in all_files {
        println!("{}: {} bytes", file.name, file.size);
    }
    Ok(())
}
```

### File Browser (nav module)

```rust
use fast_fs::nav::{Browser, BrowserConfig, KeyInput, ActionResult};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = BrowserConfig::open_dialog();
    let mut browser = Browser::new(config).await?;

    loop {
        // Render UI using browser.files(), browser.cursor(), etc.

        let key = get_user_input(); // Your framework

        match browser.handle_key(key).await {
            ActionResult::FileSelected(path) => {
                println!("Selected: {}", path.display());
                break;
            }
            ActionResult::NeedsConfirmation(op) => {
                let confirmed = ask_user(&op.message());
                browser.resolve_confirmation(confirmed).await?;
            }
            ActionResult::NeedsInput(req) => {
                let input = prompt_user(req.prompt());
                browser.complete_input(&input).await?;
            }
            _ => {}
        }
    }
    Ok(())
}
```

## API Reference

### Directory Reading Functions

```rust
use fast_fs::{read_dir, read_dir_visible, read_dir_recursive, read_dir_stream};

// Async single directory
let files = read_dir("/path").await?;

// Non-hidden files only
let visible = read_dir_visible("/path").await?;

// Recursive with options
let options = TraversalOptions::default().with_max_depth(5);
let all = read_dir_recursive("/path", options).await?;

// Streaming (memory-efficient)
let stream = read_dir_stream("/path", options);
```

### TraversalOptions

```rust
use fast_fs::TraversalOptions;

let options = TraversalOptions::default()
    .with_gitignore(true)           // Respect .gitignore (default: true)
    .with_max_depth(10)             // Limit recursion depth
    .with_extensions(&["rs", "py"]) // Filter by extension
    .with_patterns(&["*.tmp"])      // Custom ignore patterns
    .include_hidden();              // Include hidden files
```

### Navigation Module

The `nav` module provides a complete file browser component:

| Type | Description |
|------|-------------|
| `Browser` | Core state machine (owns all browser state) |
| `BrowserConfig` | Configuration with presets |
| `KeyInput` | Framework-agnostic key events |
| `KeyMap` | Configurable key bindings |
| `Action` | User actions (move, select, delete, etc.) |
| `ActionResult` | Action outcomes |
| `Selection` | Multi-selection with range support |
| `ClipboardState` | Cut/copy intent tracking |
| `History` | Back/forward navigation |
| `FileCategory` | File type categorization |

**Configuration Presets:**

```rust
use fast_fs::nav::BrowserConfig;

// Read-only file picker (shows ".." parent entry)
let config = BrowserConfig::open_dialog();

// Writable, no confirmations
let config = BrowserConfig::save_dialog();

// Full-featured explorer
let config = BrowserConfig::project_explorer();

// Customize: disable parent entry
let config = BrowserConfig::open_dialog()
    .with_show_parent_entry(false);
```

**Default Key Bindings (vim-style):**

| Key | Action |
|-----|--------|
| `j/k` or arrows | Move up/down |
| `Shift+Up/Down` | Range selection (extend) |
| `Enter/l` | Open/select |
| `h/Backspace` | Parent directory |
| `H/L` | History back/forward |
| `Space` | Toggle selection |
| `Ctrl+A` | Select all |
| `Ctrl+X` | Cut |
| `Ctrl+C` | Copy |
| `d` | Delete |
| `r` | Rename |
| `/` | Filter |
| `.` | Toggle hidden |
| `s` | Cycle sort (includes extension) |
| `PageUp/PageDown` | Page navigation (caller handles) |
| Any letter | Typeahead jump (caller handles) |

**Typeahead & Page Navigation:**

```rust
// Page navigation (caller provides viewport height)
browser.page_up(terminal_height);
browser.page_down(terminal_height);

// Windows-style typeahead: press 'a' repeatedly to cycle through 'a' files
browser.jump_to_char('a');

// Substring search: jump to files containing "doc"
browser.jump_to_substring("doc");
```

**UI Integration Helpers:**

The browser provides state accessors for building your render loop:

```rust
// Get viewport boundaries for rendering
let start = browser.scroll_offset();
let range = browser.visible_range(terminal_height);

// Render only visible files
for i in range {
    let entry = &browser.files()[i];
    let is_cursor = i == browser.cursor();
    let is_selected = browser.selection().contains(i);
    render_file_row(entry, is_cursor, is_selected);
}

// Check if parent ".." entry is shown
if browser.has_parent_entry() {
    // First entry is the parent directory
}
```

**Clipboard Model:**

The library tracks cut/copy intent; the caller handles actual clipboard and paste operations:

```rust
use fast_fs::nav::{ActionResult, ClipboardState};

// Store clipboard state when user cuts/copies
let mut app_clipboard: Option<ClipboardState> = None;

match browser.handle_key(key).await {
    ActionResult::Clipboard(state) => {
        println!("{}", state.message());  // "copy 3 items"
        app_clipboard = Some(state);
    }
    // ...
}

// Later, check for conflicts before pasting
if let Some(ref clipboard) = app_clipboard {
    let conflicts = clipboard.would_conflict(browser.current_path());
    // Caller performs actual file copy/move
}
```

**Custom Key Bindings:**

```rust
use fast_fs::nav::{KeyMap, KeyInput, Action, BrowserConfig};

// Start with defaults and customize
let mut keymap = KeyMap::default();
keymap.unbind(KeyInput::Ctrl('c'));  // Free for terminal interrupt
keymap.bind(KeyInput::Char('y'), Action::Copy);  // Use 'y' instead

let config = BrowserConfig::default().with_keymap(keymap);
```

### FileEntry

```rust
pub struct FileEntry {
    pub path: PathBuf,
    pub name: String,
    pub is_dir: bool,
    pub is_hidden: bool,
    pub size: u64,
    pub modified: Option<SystemTime>,
    pub is_symlink: bool,
    pub is_readonly: bool,
}

// Lazy methods (syscalls on demand)
entry.is_executable()      // Check execute permission
entry.resolve_symlink()    // Get symlink target
entry.is_symlink_broken()  // Check if target exists
entry.category()           // Get FileCategory
```

### FileCategory

```rust
use fast_fs::nav::FileCategory;

// Categorize by extension
let cat = FileCategory::from_extension("rs"); // Code
let cat = FileCategory::from_extension("png"); // Image

// From FileEntry (checks symlink, dir, executable first)
let cat = entry.category();
```

Categories: `Directory`, `Text`, `Code`, `Image`, `Video`, `Audio`, `Archive`, `Document`, `Executable`, `Symlink`, `Unknown`

### FileList

Versioned file list with deferred sorting:

```rust
let mut list = FileList::new();
list.push_batch(files);
list.set_sort(SortBy::Size);    // Deferred
list.set_show_hidden(true);     // Deferred
list.catchup();                 // Sort happens here
```

### SortBy

| Variant | Description |
|---------|-------------|
| `Name` / `NameDesc` | Alphabetical |
| `Size` / `SizeDesc` | By file size |
| `Modified` / `ModifiedDesc` | By modification time |
| `Extension` | By file extension |
| `DirsFirst` | Directories before files |

### GitignoreMatcher

```rust
use fast_fs::GitignoreMatcher;

let matcher = GitignoreMatcher::from_path("/project")?;

if matcher.is_ignored(&path, is_dir) {
    // Path matches .gitignore patterns
}

// Add patterns at runtime
matcher.add_pattern("*.log")?;
```

## Thread Safety

`Browser` is `Send` but not `Sync`. For shared access:

```rust
use std::sync::{Arc, Mutex};
let browser = Arc::new(Mutex::new(Browser::new(config)?));
```

## Documentation

- [API Documentation]https://docs.rs/fast-fs - Complete API reference on docs.rs
- [Quick Start Guide]https://github.com/5ocworkshop/fast-libraries/blob/main/crates/fast-fs/docs/QUICKSTART.md - Get started in 5 minutes
- [Navigation API Reference]https://github.com/5ocworkshop/fast-libraries/blob/main/crates/fast-fs/docs/API.md - Detailed nav module reference

## Testing

```bash
cargo test -p fast-fs
```

199 tests total: 76 unit tests + 113 integration tests + 10 doc tests covering directory reading, filtering, navigation, selection, clipboard, and file operations.

## License

MIT

## Author

Made with care in [Madeira, Portugal](https://visitmadeira.com/en/)

GitHub: [5ocworkshop](https://github.com/5ocworkshop)

---

> **Note on source file versions:** This crate uses [OFPF (One Function Per File)]https://github.com/5ocworkshop/ofpf methodology. Version numbers in source file headers (e.g., `VERSION: 0.5.0`) track individual file edit history, not the crate release version. The crate release version is defined in `Cargo.toml`.

<!-- <FILE>crates/fast-fs/README.md</FILE> - <DESC>fast-fs library documentation</DESC> -->
<!-- <VERS>END OF VERSION: 0.9.0</VERS> -->