Skip to content
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

Replace dirs-first with dir-order #68

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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};
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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
Expand Down
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};

Expand All @@ -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.
Expand All @@ -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};
Expand All @@ -13,6 +17,8 @@ use std::{
thread,
};

use super::order::DirectoryOrdering;

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

Expand Down Expand Up @@ -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);
}
}

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

Expand Down