dirwalk 1.1.1

Platform-optimized recursive directory walker with metadata
Documentation
use dirwalk::WalkBuilder;

#[test]
fn walk_current_dir() {
    let result = WalkBuilder::new(".").build().unwrap();
    assert!(!result.entries.is_empty());

    // All entries should have depth >= 1
    assert!(result.entries.iter().all(|e| e.depth >= 1));

    // All entries should have non-empty relative_path
    assert!(result.entries.iter().all(|e| !e.relative_path.is_empty()));
}

#[test]
fn walk_with_stats() {
    let result = WalkBuilder::new(".").stats(true).build().unwrap();
    let stats = result.stats.as_ref().unwrap();

    assert!(stats.file_count > 0);
    assert!(stats.total_size > 0);
    assert!(stats.file_count + stats.dir_count == result.entries.len() as u64);
}

#[test]
fn walk_max_depth_1() {
    let result = WalkBuilder::new(".").max_depth(1).build().unwrap();

    // All entries should be at depth 1 (direct children only)
    assert!(result.entries.iter().all(|e| e.depth == 1));
}

#[test]
fn walk_max_depth_limits_recursion() {
    let shallow = WalkBuilder::new(".").max_depth(1).build().unwrap();
    let deep = WalkBuilder::new(".").build().unwrap();

    // Unlimited walk should find at least as many entries
    assert!(deep.entries.len() >= shallow.entries.len());
}

#[test]
fn walk_hidden_excluded_by_default() {
    let result = WalkBuilder::new(".").build().unwrap();
    let has_hidden = result.entries.iter().any(|e| e.is_hidden);
    assert!(!has_hidden, "hidden entries should be excluded by default");
}

#[cfg(unix)]
#[test]
fn walk_hidden_included_when_requested() {
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join("visible.txt"), "x").unwrap();
    std::fs::write(dir.path().join(".hidden"), "x").unwrap();

    let without = WalkBuilder::new(dir.path()).hidden(false).build().unwrap();
    let with_hidden = WalkBuilder::new(dir.path()).hidden(true).build().unwrap();

    assert_eq!(
        without.entries.len(),
        1,
        "hidden(false) should exclude .hidden"
    );
    assert_eq!(
        with_hidden.entries.len(),
        2,
        "hidden(true) should include .hidden"
    );
}

#[cfg(windows)]
#[test]
fn walk_hidden_included_when_requested() {
    use std::os::windows::ffi::OsStrExt;
    let dir = tempfile::tempdir().unwrap();
    std::fs::write(dir.path().join("visible.txt"), "x").unwrap();
    let hidden_path = dir.path().join(".hidden");
    std::fs::write(&hidden_path, "x").unwrap();

    // Set FILE_ATTRIBUTE_HIDDEN so the scanner actually considers it hidden
    let wide: Vec<u16> = hidden_path
        .as_os_str()
        .encode_wide()
        .chain(std::iter::once(0))
        .collect();
    unsafe {
        windows::Win32::Storage::FileSystem::SetFileAttributesW(
            windows::core::PCWSTR(wide.as_ptr()),
            windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN,
        )
        .unwrap();
    }

    let without = WalkBuilder::new(dir.path()).hidden(false).build().unwrap();
    let with_hidden = WalkBuilder::new(dir.path()).hidden(true).build().unwrap();

    assert_eq!(
        without.entries.len(),
        1,
        "hidden(false) should exclude .hidden"
    );
    assert_eq!(
        with_hidden.entries.len(),
        2,
        "hidden(true) should include .hidden"
    );
}

#[test]
fn walk_relative_paths_are_correct() {
    let result = WalkBuilder::new(".").max_depth(2).build().unwrap();

    for entry in &result.entries {
        // relative_path should end with the entry name
        assert_eq!(
            entry.name(),
            entry.relative_path.rsplit(['/', '\\']).next().unwrap()
        );

        // depth should match path component count
        let components: usize = entry.relative_path.split(['/', '\\']).count();
        assert_eq!(components, entry.depth as usize);
    }
}

#[test]
fn walk_invalid_path() {
    let result = WalkBuilder::new("nonexistent_path_xyz").build();
    assert!(result.is_err());
}