dirwalk 1.1.1

Platform-optimized recursive directory walker with metadata
Documentation
//! Sorting for walk results.
//!
//! Entries sharing the same parent directory are sorted relative to each
//! other while preserving the overall tree ordering (parent before children).
//! Supports natural sort on names (e.g. `file2` < `file10`) via [`natord`].

use crate::entry::Entry;
use crate::util::parent_dir;

#[derive(Clone, Copy)]
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
pub enum Sort {
    Name,
    Modified,
    Size,
    Extension,
}

/// Sort entries. Entries sharing the same parent are sorted relative to each other.
/// The overall tree ordering (parent before children) is preserved.
///
/// NOTE: assumes entries with the same parent are contiguous in the slice.
/// If that invariant is violated, groups are split and sorted independently.
pub fn sort_entries(entries: &mut [Entry], sort: Sort, dirs_first: bool) {
    let mut start = 0;
    while start < entries.len() {
        let parent = parent_dir(&entries[start].relative_path);
        let mut end = start + 1;
        while end < entries.len() && parent_dir(&entries[end].relative_path) == parent {
            end += 1;
        }

        entries[start..end].sort_by(|a, b| {
            if dirs_first {
                let dir_ord = b.is_dir.cmp(&a.is_dir);
                if dir_ord != std::cmp::Ordering::Equal {
                    return dir_ord;
                }
            }

            match sort {
                Sort::Name => natord::compare(a.name(), b.name()),
                Sort::Modified => b.modified.cmp(&a.modified),
                Sort::Size => b.size.cmp(&a.size),
                Sort::Extension => {
                    let a_ext = a.extension().unwrap_or("");
                    let b_ext = b.extension().unwrap_or("");
                    a_ext
                        .cmp(b_ext)
                        .then_with(|| natord::compare(a.name(), b.name()))
                }
            }
        });

        start = end;
    }
}