From 25ab1f0639246a4622c0561abe6acb54c686df51 Mon Sep 17 00:00:00 2001
From: fawn <fawn@envs.net>
Date: Tue, 14 Mar 2023 03:11:43 +0200
Subject: [PATCH 1/2] allow `dirs-first` flag to work regardless of order

---
 src/render/order.rs    |  4 ++++
 src/render/tree/mod.rs | 10 +++++++++-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/render/order.rs b/src/render/order.rs
index a2e0e8f7..ff606a79 100644
--- a/src/render/order.rs
+++ b/src/render/order.rs
@@ -13,6 +13,9 @@ pub enum SortType {
 
     /// Sort entries by size largest to smallest, bottom to top
     SizeRev,
+
+    /// Do not sort entries
+    None,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -56,6 +59,7 @@ impl SortType {
             Self::Name => Some(Box::new(Self::name_comparator)),
             Self::Size => Some(Box::new(Self::size_comparator)),
             Self::SizeRev => Some(Box::new(Self::size_rev_comparator)),
+            Self::None => None,
         }
     }
 
diff --git a/src/render/tree/mod.rs b/src/render/tree/mod.rs
index dea93f95..c1dbdbd9 100644
--- a/src/render/tree/mod.rs
+++ b/src/render/tree/mod.rs
@@ -1,4 +1,8 @@
-use crate::render::{context::Context, disk_usage::FileSize, order::Order};
+use crate::render::{
+    context::Context,
+    disk_usage::FileSize,
+    order::{Order, SortType},
+};
 use crossbeam::channel::{self, Sender};
 use error::Error;
 use ignore::{WalkBuilder, WalkParallel, WalkState};
@@ -172,6 +176,10 @@ impl Tree {
         if let Some(ordr) = ctx.sort().map(|s| Order::from((s, ctx.dirs_first()))) {
             ordr.comparator()
                 .map(|func| current_node.sort_children(func));
+        } else if ctx.dirs_first() {
+            Order::from((SortType::None, true))
+                .comparator()
+                .map(|func| current_node.sort_children(func));
         }
     }
 }

From 33f171464a56f298b096d5f2f4557d4583aee576 Mon Sep 17 00:00:00 2001
From: xiuxiu62 <jacremer@live.com>
Date: Mon, 13 Mar 2023 21:59:33 -0700
Subject: [PATCH 2/2] replaces dir-first with dir-order

---
 src/render/context/mod.rs |  14 ++---
 src/render/order.rs       | 108 +++++++++++++++++++++++++-------------
 src/render/tree/mod.rs    |  14 +++--
 src/render/tree/node.rs   |   2 +-
 4 files changed, 86 insertions(+), 52 deletions(-)

diff --git a/src/render/context/mod.rs b/src/render/context/mod.rs
index 7cd1e3c8..a54a7f66 100644
--- a/src/render/context/mod.rs
+++ b/src/render/context/mod.rs
@@ -1,6 +1,6 @@
 use super::{
     disk_usage::{DiskUsage, PrefixKind},
-    order::SortType,
+    order::{DirectoryOrdering, SortType},
 };
 use clap::{ArgMatches, CommandFactory, Error as ClapError, FromArgMatches, Parser};
 use ignore::overrides::{Override, OverrideBuilder};
@@ -81,9 +81,9 @@ pub struct Context {
     #[arg(short, long, value_enum)]
     sort: Option<SortType>,
 
-    /// Always sorts directories above files
-    #[arg(long)]
-    dirs_first: bool,
+    /// Orders directories within branch arms
+    #[arg(short = 'D', long, value_name = "ORDER")]
+    dir_order: Option<DirectoryOrdering>,
 
     /// Traverse symlink directories and consider their disk usage; disabled by default
     #[arg(short = 'S', long)]
@@ -151,9 +151,9 @@ impl Context {
         self.sort
     }
 
-    /// Getter for `dirs_first` field.
-    pub fn dirs_first(&self) -> bool {
-        self.dirs_first
+    /// Getter for `dir_order` field.
+    pub fn dir_ordering(&self) -> Option<DirectoryOrdering> {
+        self.dir_order
     }
 
     /// The max depth to print. Note that all directories are fully traversed to compute file
diff --git a/src/render/order.rs b/src/render/order.rs
index ff606a79..20bf6fce 100644
--- a/src/render/order.rs
+++ b/src/render/order.rs
@@ -1,4 +1,4 @@
-use super::tree::node::Node;
+use super::{context::Context, tree::node::Node};
 use clap::ValueEnum;
 use std::{cmp::Ordering, convert::From};
 
@@ -13,54 +13,51 @@ pub enum SortType {
 
     /// Sort entries by size largest to smallest, bottom to top
     SizeRev,
+}
+
+/// Order in which to print directories.
+#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
+pub enum DirectoryOrdering {
+    /// Order directories before files
+    First,
 
-    /// Do not sort entries
-    None,
+    /// Order directories after files
+    Last,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Order {
-    sort: SortType,
-    dir_first: bool,
+    sort: Option<SortType>,
+    dir: Option<DirectoryOrdering>,
 }
 
 /// Comparator type used to sort [Node]s.
-pub type NodeComparator<'a> = dyn Fn(&Node, &Node) -> Ordering + 'a;
+pub type NodeComparator = dyn Fn(&Node, &Node) -> Ordering;
 
 impl Order {
     /// Yields function pointer to the appropriate `Node` comparator.
-    pub fn comparator(&self) -> Option<Box<NodeComparator<'_>>> {
-        if self.dir_first {
-            return Some(Box::new(|a, b| {
-                Self::dir_comparator(a, b, self.sort.comparator())
-            }));
-        }
-
-        self.sort.comparator()
-    }
-
-    fn dir_comparator(
-        a: &Node,
-        b: &Node,
-        fallback: Option<impl Fn(&Node, &Node) -> Ordering>,
-    ) -> Ordering {
-        match (a.is_dir(), b.is_dir()) {
-            (true, false) => Ordering::Less,
-            (false, true) => Ordering::Greater,
-            _ => fallback.map_or_else(|| Ordering::Equal, |sort| sort(a, b)),
-        }
+    pub fn comparators(&self) -> impl Iterator<Item = Box<NodeComparator>> {
+        [
+            self.sort.as_ref().map(SortType::comparator),
+            self.dir.as_ref().map(DirectoryOrdering::comparator),
+        ]
+        .into_iter()
+        .filter(|comparator| comparator.is_some())
+        // UNWRAP: we just filtered Nones out
+        .map(|comparator| comparator.unwrap())
     }
 }
 
 impl SortType {
     /// Yields function pointer to the appropriate `Node` comparator.
-    pub fn comparator(&self) -> Option<Box<dyn Fn(&Node, &Node) -> Ordering>> {
-        match self {
-            Self::Name => Some(Box::new(Self::name_comparator)),
-            Self::Size => Some(Box::new(Self::size_comparator)),
-            Self::SizeRev => Some(Box::new(Self::size_rev_comparator)),
-            Self::None => None,
-        }
+    pub fn comparator(&self) -> Box<dyn Fn(&Node, &Node) -> Ordering> {
+        let comparator = match self {
+            Self::Name => Self::name_comparator,
+            Self::Size => Self::size_comparator,
+            Self::SizeRev => Self::size_rev_comparator,
+        };
+
+        Box::new(comparator)
     }
 
     /// Comparator based on `Node` file names.
@@ -84,8 +81,47 @@ impl SortType {
     }
 }
 
-impl From<(SortType, bool)> for Order {
-    fn from((sort, dir_first): (SortType, bool)) -> Self {
-        Order { sort, dir_first }
+impl DirectoryOrdering {
+    /// Yields function pointer to the appropriate directory comparator.
+    pub fn comparator(&self) -> Box<NodeComparator> {
+        let comparator = match self {
+            Self::First => Self::first_comparator,
+            Self::Last => Self::last_comparator,
+        };
+
+        Box::new(comparator)
+    }
+
+    /// Comparator based on directory presedence.
+    fn first_comparator(a: &Node, b: &Node) -> Ordering {
+        match (a.is_dir(), b.is_dir()) {
+            (true, false) => Ordering::Less,
+            (false, true) => Ordering::Greater,
+            _ => Ordering::Equal,
+        }
+    }
+
+    /// Comparator based on non-directory presedence.
+    fn last_comparator(a: &Node, b: &Node) -> Ordering {
+        match (a.is_dir(), b.is_dir()) {
+            (false, true) => Ordering::Less,
+            (true, false) => Ordering::Greater,
+            _ => Ordering::Equal,
+        }
+    }
+}
+
+impl<'a> From<&'a Context> for Order {
+    fn from(ctx: &'a Context) -> Self {
+        Self {
+            sort: ctx.sort(),
+            dir: ctx.dir_ordering(),
+        }
+    }
+}
+
+impl From<(Option<SortType>, Option<DirectoryOrdering>)> for Order {
+    fn from((sort, dir): (Option<SortType>, Option<DirectoryOrdering>)) -> Self {
+        Self { sort, dir }
     }
 }
diff --git a/src/render/tree/mod.rs b/src/render/tree/mod.rs
index c1dbdbd9..25e4626f 100644
--- a/src/render/tree/mod.rs
+++ b/src/render/tree/mod.rs
@@ -17,6 +17,8 @@ use std::{
     thread,
 };
 
+use super::order::DirectoryOrdering;
+
 /// Errors related to traversal, [Tree] construction, and the like.
 pub mod error;
 
@@ -173,14 +175,10 @@ impl Tree {
             current_node.set_file_size(dir_size)
         }
 
-        if let Some(ordr) = ctx.sort().map(|s| Order::from((s, ctx.dirs_first()))) {
-            ordr.comparator()
-                .map(|func| current_node.sort_children(func));
-        } else if ctx.dirs_first() {
-            Order::from((SortType::None, true))
-                .comparator()
-                .map(|func| current_node.sort_children(func));
-        }
+        let apply_comparator = |comparator| current_node.sort_children(comparator);
+        Order::from((ctx.sort(), ctx.dir_ordering()))
+            .comparators()
+            .for_each(apply_comparator);
     }
 }
 
diff --git a/src/render/tree/node.rs b/src/render/tree/node.rs
index c277a08d..85650c67 100644
--- a/src/render/tree/node.rs
+++ b/src/render/tree/node.rs
@@ -86,7 +86,7 @@ impl Node {
     }
 
     /// Sorts `children` given comparator.
-    pub fn sort_children(&mut self, comparator: Box<NodeComparator<'_>>) {
+    pub fn sort_children(&mut self, comparator: Box<NodeComparator>) {
         self.children.sort_by(comparator)
     }