Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Filesystem summary

Aggregate file sizes, counts, and directory depth in one pass. The heap is a structured Summary — multiple metrics accumulated simultaneously.

#![allow(unused)]
fn main() {
//! Filesystem tree summary — structured heap accumulating multiple metrics.

#[cfg(test)]
mod tests {
    use hylic::prelude::*;
    use insta::assert_snapshot;


    /// A filesystem entry: either a file (leaf) or a directory (branch).
    #[derive(Clone)]
    #[allow(dead_code)]
    enum FsEntry {
        File { name: String, size: u64 },
        Dir { name: String, children: Vec<FsEntry> },
    }

    impl FsEntry {
        fn file(name: &str, size: u64) -> Self {
            FsEntry::File { name: name.into(), size }
        }
        fn dir(name: &str, ch: Vec<FsEntry>) -> Self {
            FsEntry::Dir { name: name.into(), children: ch }
        }
    }

    /// Accumulates size, file count, and directory count in one pass.
    #[derive(Clone, Debug, PartialEq)]
    struct Summary {
        total_size: u64,
        file_count: usize,
        dir_count: usize,
    }

    #[test]
    fn summarize_filesystem() {
        let tree = FsEntry::dir("project", vec![
            FsEntry::file("README.md", 1200),
            FsEntry::dir("src", vec![
                FsEntry::file("main.rs", 5000),
                FsEntry::file("lib.rs", 3000),
                FsEntry::dir("utils", vec![
                    FsEntry::file("helpers.rs", 800),
                ]),
            ]),
            FsEntry::file("Cargo.toml", 400),
        ]);

        // Files are implicit leaves; only directories produce children.
        let graph: Treeish<FsEntry> = treeish_visit(|entry: &FsEntry, cb: &mut dyn FnMut(&FsEntry)| {
            if let FsEntry::Dir { children, .. } = entry {
                for child in children { cb(child); }
            }
        });

        // Structured heap — multiple metrics tracked in one pass. H = R = Summary.
        let summarize: Fold<FsEntry, Summary, Summary> = fold(
            |entry: &FsEntry| match entry {
                FsEntry::File { size, .. } =>
                    Summary { total_size: *size, file_count: 1, dir_count: 0 },
                FsEntry::Dir { .. } =>
                    Summary { total_size: 0, file_count: 0, dir_count: 1 },
            },
            |heap: &mut Summary, child: &Summary| {
                heap.total_size += child.total_size;
                heap.file_count += child.file_count;
                heap.dir_count += child.dir_count;
            },
            |h: &Summary| h.clone(),
        );

        let result: Summary = FUSED.run(&summarize, &graph, &tree);
        assert_eq!(result, Summary {
            total_size: 10400, file_count: 5, dir_count: 3,
        });

        assert_snapshot!("fs_summary", format!(
            "project/: {} bytes, {} files, {} dirs",
            result.total_size, result.file_count, result.dir_count,
        ));
    }
}
}

Output:

project/: 10400 bytes, 5 files, 3 dirs