Skip to content

Replace dirs-first with dir-order #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/render/context/mod.rs
Original file line number Diff line number Diff line change
@@ -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
106 changes: 73 additions & 33 deletions src/render/order.rs
Original file line number Diff line number Diff line change
@@ -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};

@@ -15,48 +15,49 @@ pub enum SortType {
SizeRev,
}

/// Order in which to print directories.
#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
pub enum DirectoryOrdering {
/// Order directories before files
First,

/// 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)),
}
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.
@@ -80,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 }
}
}
16 changes: 11 additions & 5 deletions src/render/tree/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -13,6 +17,8 @@ use std::{
thread,
};

use super::order::DirectoryOrdering;

/// Errors related to traversal, [Tree] construction, and the like.
pub mod error;

@@ -169,10 +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));
}
let apply_comparator = |comparator| current_node.sort_children(comparator);
Order::from((ctx.sort(), ctx.dir_ordering()))
.comparators()
.for_each(apply_comparator);
}
}

2 changes: 1 addition & 1 deletion src/render/tree/node.rs
Original file line number Diff line number Diff line change
@@ -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)
}