walkdir_minimal 1.1.0

Fast, minimalist, and POSIX-compliant recursive directory traverser with zero external dependencies.
Documentation
  • Coverage
  • 100%
    10 out of 10 items documented0 out of 0 items with examples
  • Size
  • Source code size: 194.06 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.01 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 11s Average build duration of successful builds.
  • all releases: 11s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • LinuxProativo/walkdir_minimal
    1 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • LinuxProativo

πŸ” Overview

walkdir_minimal is a lightweight, POSIX-only directory walker written in 100% safe Rust, designed for maximum portability, robust error handling, and predictable iteration order across UNIX-like systems.

Unlike the popular walkdir crate, which offers extensive configurability and Windows support, walkdir_minimal aims to provide a clean, dependency-free and fully deterministic implementation that follows the UNIX filesystem model precisely β€” no abstractions, no hidden buffering, no non-POSIX extensions.

✨ Key Features

  • 🧱 POSIX-only: Works on Linux, FreeBSD, OpenBSD, NetBSD, and Solaris.

  • βš™οΈ No dependencies: Implemented using only std::fs, std::path, and minimal data structures.

  • 🦦 Lightweight and predictable: The walker uses a manual stack (no recursion), allowing predictable memory and performance behavior.

  • πŸ¦‰ Configurable options via WalkOptions:

    • follow_links: whether to follow symbolic links to directories.
    • max_depth: optional limit on traversal depth.
  • 🧠 Cycle detection: Detects and prevents infinite loops caused by symbolic links that form cycles.

  • 🚫 Graceful handling of I/O errors: Broken symlinks, permission-denied directories, and other errors are returned as Err(WalkError::Io).

  • 🦦 Filtering: Supports entry-level filtering with a user-provided closure.

  • 🧫 Deterministic: The order of traversal follows the order provided by the filesystem’s readdir(3) implementation β€” consistent across runs on the same system.

  • πŸ§ͺ Minimal yet robust: Designed for projects that require reliable, low-level control rather than high-level abstraction.

πŸͺΆ Design Philosophy

walkdir_minimal is built under the following principles:

  1. POSIX compliance first β€” all filesystem operations map directly to their POSIX equivalents (lstat, stat, opendir, readdir, etc., via Rust’s std::fs).
  2. Deterministic behavior β€” the iterator never hides errors, skips entries silently, or spawns threads.
  3. No allocations beyond what’s necessary β€” uses Vec for the manual stack and HashSet for visited inode/device pairs (loop detection).
  4. No recursion β€” prevents stack overflows and maintains stable memory usage even for deeply nested trees.
  5. Minimalism β€” the crate is intentionally limited to features that can be reasoned about and verified easily.
  6. Transparency β€” the API surfaces raw I/O results instead of silently ignoring or swallowing them.

walkdir_minimal embodies clarity over complexity. Its goal is not to compete with feature-rich crates, but to provide a clean reference implementation of a POSIX-only directory walker.

βš–οΈ Comparison with walkdir

Feature walkdir walkdir_minimal
Cross-platform βœ… (Windows, macOS, Linux) ❌ POSIX only
Dependencies Many (e.g., same-file, winapi) ❌ None
Error handling Complex iterator states Simple Result<Entry, WalkError>
Loop detection Optional, platform-specific Deterministic (dev, ino) hashing
Symbolic links Optional follow Optional follow
Custom sorting Supported Not supported (filesystem order only)
Performance Optimized for general use Optimized for predictability
Safety 100% safe Rust 100% safe Rust
Recursion Implicit Manual stack
Binary size Larger Tiny
Filter API Supported (filter_entry) Supported
Error type walkdir::Error WalkError
Metadata caching Yes No (on-demand)
Thread safety Yes No (intentionally minimal)

πŸ¦‰ Example Usage

use walkdir_minimal::{WalkDir, WalkError};

fn main() -> Result<(), WalkError> {
    for entry in WalkDir::new(".")?.follow_links(false) {
        match entry {
            Ok(e) => println!("{}", e.path().display()),
            Err(err) => eprintln!("Error: {}", err),
        }
    }
    Ok(())
}

Output example:

.
./src
./src/lib.rs
./src/entry.rs
./src/error.rs
./src/walkdir.rs

πŸ“¦ WalkOptions

#[derive(Clone, Debug)]
pub struct WalkOptions {
    pub follow_links: bool,
    pub max_depth: usize,
}
  • follow_links β€” When true, symbolic links to directories are followed.

  • max_depth β€” Optional limit to recursion depth. None means unlimited.

    • The root is always depth 0.
    • Files or subdirectories at one level below are depth 1, and so on.

πŸ”— Entry API

pub struct Entry {
    path: PathBuf,
    depth: usize,
}

impl Entry {
    pub fn path(&self) -> &Path;
    pub fn depth(&self) -> usize;
    pub fn metadata(&self) -> io::Result<fs::Metadata>;
    pub fn symlink_metadata(&self) -> io::Result<fs::Metadata>;
    pub fn file_type(&self) -> io::Result<fs::FileType>;
}
  • metadata() calls fs::metadata, following symlinks.
  • symlink_metadata() calls fs::symlink_metadata, not following symlinks.
  • file_type() reports the symbolic link type correctly.

πŸ¦‰ Error Handling

pub enum WalkError {
    Io(io::Error),
    LoopDetected(PathBuf),
}
  • Io(io::Error) β€” Covers all I/O-related errors, including:

    • Broken symbolic links (ENOENT)
    • Permission-denied directories (EACCES)
    • Filesystem read errors
  • LoopDetected(PathBuf) β€” Reported when a cyclic symbolic link is detected (only if loop detection is enabled).

βš™οΈ Default Behavior Summary

Case Behavior
Broken symlink Yields Err(WalkError::Io)
Permission denied directory Yields Err(WalkError::Io) and continues
Loop via symlink Yields Err(WalkError::LoopDetected) if detection is on
Regular file as root Returns file directly, no traversal
Unreadable entry Returns Err(WalkError::Io)
Exceeds max_depth Skips entry silently (depth-guarded)

πŸ” Technical Details

πŸ“Œ Core Design

walkdir_minimal implements a depth-first directory traversal without relying on any external dependencies, using only POSIX APIs available through Rust’s standard library. The iterator is built around a manual stack-based traversal that mimics recursion, avoiding stack overflows for deeply nested directories.

  • Stack-based iteration: Uses an internal vector of StackEntry structs, each holding an active ReadDir handle and its depth.
  • Loop detection: Uses a HashSet<(dev, ino)> to detect and skip cyclic symlinks, preventing infinite recursion.
  • Filter callbacks: Optional user-provided closures (filter_entry) allow pruning of the traversal tree dynamically.
  • Error resilience: Each I/O operation is wrapped in Result, and errors are surfaced as WalkError variants (Io, LoopDetected).

πŸ“Œ Error Handling Philosophy

walkdir_minimal follows a fail-soft philosophy:

  • Broken symlinks are returned as Ok(Entry) unless metadata is explicitly requested.
  • Directories without permission to read (EACCES) return an Err(WalkError::Io), allowing iteration to continue with the next entry.
  • Files disappearing mid-iteration yield Err(WalkError::Io) gracefully.

This mirrors walkdir’s behavior but keeps it predictable and minimal.

πŸ“Œ Metadata Access

Entry deliberately does not cache metadata by default. This ensures:

  • Minimal memory overhead.
  • Consistent behavior with file system changes.
  • Full control for users who may wish to query metadata() or symlink_metadata() selectively.
let entry = Entry::new(path, depth);
if let Ok(meta) = entry.metadata() {
    println!("File size: {} bytes", meta.len());
}

πŸ“Œ Platform Scope

walkdir_minimal targets POSIX systems only β€” this includes:

  • GNU/Linux
  • *BSD family (FreeBSD, OpenBSD, NetBSD, DragonFly)
  • Solaris and Illumos

It relies on MetadataExt for device/inode access, which is non-portable to Windows. No attempt is made to support non-POSIX environments.

πŸ“Œ Performance Characteristics

  • Single ReadDir handle open at a time per stack frame.
  • Minimal heap allocations aside from the stack and visited set.
  • No synchronization primitives β€” designed for single-threaded deterministic traversal.
  • Filtering and loop detection incur negligible overhead for typical file trees.

πŸ“Œ Safety & Reliability

  • No unsafe code.
  • Uses standard library types exclusively (HashSet, Vec, ReadDir, etc.).
  • All system calls are wrapped in safe Rust abstractions.
  • Loop detection ensures full reliability even on pathological file systems.

πŸ“š Practical Use Cases

  • Static analysis tools.
  • POSIX-friendly installers and archivers.
  • File packers and dependency scanners.
  • System recovery tools that must run without external crates.

Example: skipping hidden files and following symlinks safely:

use walkdir_minimal::WalkDir;

let iter = WalkDir::new("/usr")
    .unwrap()
    .follow_links(true)
    .filter_entry(|e| !e.path().file_name().map(|n| n.to_string_lossy().starts_with('.')).unwrap_or(false));

for entry in iter {
    match entry {
        Ok(e) => println!("{}", e.path().display()),
        Err(err) => eprintln!("Error: {}", err),
    }
}

πŸ’‘ Why Choose walkdir_minimal?

  • Ideal for small binaries, system utilities, and initramfs tools.
  • Zero build dependencies (fast compile times).
  • Deterministic and predictable traversal order.
  • Designed to be readable and hackable.

🧩 Implementation Notes

  • Loop detection uses (dev, ino) pairs to identify unique directories.
  • When follow_links is disabled, symlink loops are naturally impossible.
  • max_depth limits traversal, excluding deeper entries.
  • The iterator yields entries as soon as they are discovered β€” no preloading or buffering.

🀝 Contributing

Contributions are very welcome! Whether it’s fixing a bug, improving documentation, or adding new features that align with the minimalist and POSIX-only philosophy, your input is appreciated.

Please follow these guidelines when contributing:

  1. Keep it minimal β€” avoid adding dependencies or non-POSIX abstractions.
  2. Preserve safety β€” no unsafe code will be accepted.
  3. Document behavior clearly β€” especially for error cases and edge conditions.
  4. Add tests β€” every feature or bug fix should include a minimal test.

If you find a bug or have a suggestion for improvement:

  • Open an issue describing the behavior or proposal clearly.
  • Include steps to reproduce (for bugs) or examples (for feature requests).

Pull requests should target the main branch and include clear commit messages.

πŸ§ͺ Testing

walkdir_minimal includes tests for common scenarios:

  • Regular files and nested directories.
  • Symbolic link loops and detection.
  • Permission-denied directories.
  • Broken symbolic links.
  • Filtered traversals and depth limits.

Run the test suite with:

cargo test

🧱 Project Structure

walkdir_minimal/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ lib.rs           # Main crate entry
β”‚   β”œβ”€β”€ entry.rs         # Defines the Entry type
β”‚   β”œβ”€β”€ error.rs         # WalkError and error utilities
β”‚   β”œβ”€β”€ options.rs       # WalkOptions definition
β”‚   β”œβ”€β”€ tests.rs         # Unit and integration tests
β”‚   └── walkdir.rs       # Core iterator implementation
β”œβ”€β”€ README.md            # Project documentation
β”œβ”€β”€ LICENSE              # License file (MIT)
└── Cargo.toml           # Package metadata

πŸ“œ MIT License

This repository has scripts that were created to be free software. Therefore, they can be distributed and/or modified within the terms of the MIT License.

See the LICENSE file for details.

πŸ“¬ Contact & Support