parallel-disk-usage 0.6.1

Highly parallelized, blazing fast directory tree analyzer
Documentation
use crate::{data_tree::DataTree, size::Bytes};
use pretty_assertions::assert_eq;
use std::{cmp::Ordering, ops::Not};

type SampleName = String;
type SampleData = Bytes;
type SampleTree = DataTree<SampleName, SampleData>;

fn dir<const INODE_SIZE: u64>(name: &'static str, children: Vec<SampleTree>) -> SampleTree {
    SampleTree::dir(name.to_string(), INODE_SIZE.into(), children)
}

fn file(name: &'static str, size: u64) -> SampleTree {
    SampleTree::file(name.to_string(), size.into())
}

fn culled_dir<const INODE_SIZE: u64>(
    name: &'static str,
    culled_size: u64,
    children: Vec<SampleTree>,
) -> SampleTree {
    SampleTree::dir(
        name.to_string(),
        (INODE_SIZE + culled_size).into(),
        children,
    )
}

fn order_tree(left: &SampleTree, right: &SampleTree) -> Ordering {
    left.name().cmp(right.name())
}

#[test]
fn typical_case() {
    let dir = dir::<4069>;
    let culled_dir = culled_dir::<4069>;
    let actual = dir(
        "root",
        vec![
            dir("empty directory", vec![]),
            dir("directory of one empty file", vec![file("empty file", 0)]),
            dir(
                "directory of one small file",
                vec![file("small file", 4321)],
            ),
            dir("directory of one big file", vec![file("big file", 54321)]),
            dir(
                "directory of multiple small files",
                vec![
                    file("a", 4321),
                    file("b", 4321),
                    file("c", 4321),
                    file("d", 4321),
                    file("e", 4321),
                    file("f", 4321),
                    file("g", 4321),
                    file("h", 4321),
                    file("i", 4321),
                    file("j", 4321),
                    file("k", 4321),
                ],
            ),
            dir(
                "directory of multiple files of various sizes",
                vec![
                    file("a", 4321),
                    file("b", 4321),
                    file("c", 4321),
                    file("d", 4321),
                    file("e", 4321),
                    file("f", 4321),
                    file("g", 4321),
                    file("h", 4321),
                    file("i", 4321),
                    file("j", 4321),
                    file("A", 54321),
                    file("B", 54321),
                    file("C", 54321),
                    file("D", 54321),
                    file("E", 54321),
                    file("F", 54321),
                ],
            ),
        ],
    )
    .into_par_sorted(order_tree)
    .into_insignificant_data_par_culled(0.05)
    .into_reflection();
    let expected = culled_dir(
        "root",
        4069 + 4069 + (4069 + 4321),
        vec![
            culled_dir(
                "directory of multiple files of various sizes",
                10 * 4321,
                vec![
                    file("A", 54321),
                    file("B", 54321),
                    file("C", 54321),
                    file("D", 54321),
                    file("E", 54321),
                    file("F", 54321),
                ],
            ),
            file("directory of multiple small files", 4069 + 11 * 4321),
            dir("directory of one big file", vec![file("big file", 54321)]),
        ],
    )
    .into_reflection();
    assert_eq!(actual, expected);
}

#[test]
fn edge_cases() {
    let dir = dir::<4069>;
    let culled_dir = culled_dir::<4069>;
    let actual = dir(
        "root",
        vec![
            dir(
                "reduce half",
                vec![
                    file("!abc", 123),
                    file("abc", 321),
                    file("!def", 456),
                    file("def", 654),
                    file("!ghi", 789),
                    file("ghi", 987),
                ],
            ),
            dir(
                "reduce all",
                vec![file("!abc", 123), file("!def", 456), file("!ghi", 789)],
            ),
            dir(
                "reduce none",
                vec![file("abc", 321), file("def", 654), file("ghi", 987)],
            ),
            dir(
                "reduce one",
                vec![
                    file("abc", 321),
                    file("def", 654),
                    file("!def", 456),
                    file("ghi", 987),
                ],
            ),
            dir(
                "reduce all but one",
                vec![
                    file("!abc", 123),
                    file("!def", 456),
                    file("!ghi", 789),
                    file("def", 654),
                ],
            ),
        ],
    )
    .into_par_retained(|descendant| descendant.name().starts_with('!').not())
    .into_reflection();
    let expected = dir(
        "root",
        vec![
            culled_dir(
                "reduce half",
                123 + 456 + 789,
                vec![file("abc", 321), file("def", 654), file("ghi", 987)],
            ),
            file("reduce all", 4069 + 123 + 456 + 789),
            dir(
                "reduce none",
                vec![file("abc", 321), file("def", 654), file("ghi", 987)],
            ),
            culled_dir(
                "reduce one",
                456,
                vec![file("abc", 321), file("def", 654), file("ghi", 987)],
            ),
            culled_dir(
                "reduce all but one",
                123 + 456 + 789,
                vec![file("def", 654)],
            ),
        ],
    )
    .into_reflection();
    assert_eq!(actual, expected);
}