summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Nguyen <benjamin.van.nguyen@gmail.com>2023-11-27 13:37:46 -0800
committerBenjamin Nguyen <benjamin.van.nguyen@gmail.com>2023-11-27 13:37:46 -0800
commitef7b333ac9635cdf2cd78b204cedf42a2da8fcc1 (patch)
tree56c96544cd443336d8981e3b797a431b5a9e9a6e
parent9c62bb3f4c3d78eeb379840afb105d9d26946d1d (diff)
order wip
-rw-r--r--src/file/mod.rs7
-rw-r--r--src/file/order.rs153
-rw-r--r--src/file/tree/mod.rs30
-rw-r--r--src/render/mod.rs14
-rw-r--r--src/user/args.rs58
-rw-r--r--src/user/mod.rs8
6 files changed, 251 insertions, 19 deletions
diff --git a/src/file/mod.rs b/src/file/mod.rs
index 70ac5d8..d307022 100644
--- a/src/file/mod.rs
+++ b/src/file/mod.rs
@@ -18,6 +18,9 @@ use std::{
pub mod inode;
use inode::{INodeError, Inode};
+/// Rules on how to order entries relative to their siblings or all other files.
+pub mod order;
+
/// Concerned with the tree data structure that is used to produce the program output.
pub mod tree;
pub use tree::Tree;
@@ -161,6 +164,10 @@ impl File {
})
.map(|dt| format!("{dt}"))
}
+
+ pub fn is_dir(&self) -> bool {
+ self.file_type().is_some_and(|ft| ft.is_dir())
+ }
}
impl Deref for File {
diff --git a/src/file/order.rs b/src/file/order.rs
new file mode 100644
index 0000000..c982147
--- /dev/null
+++ b/src/file/order.rs
@@ -0,0 +1,153 @@
+use super::File;
+use crate::user::{args::{Sort, DirOrder}, Context};
+use std::cmp::Ordering;
+
+/// Comparator type used to sort [File]s.
+pub type FileComparator = dyn Fn(&File, &File) -> Ordering;
+
+/// Yields function pointer to the appropriate `File` comparator.
+pub fn comparator(Context { sort, dir_order, .. }: &Context) -> Option<Box<FileComparator>> {
+ if matches!(sort, Sort::None) {
+ return None;
+ }
+
+ match dir_order {
+ DirOrder::First => {
+ Some(Box::new(move |a, b| dir_first_comparator(a, b, base_comparator(*sort))))
+ },
+ DirOrder::Last => {
+ Some(Box::new(move |a, b| dir_last_comparator(a, b, base_comparator(*sort))))
+ },
+ DirOrder::None => Some(base_comparator(*sort)),
+ }
+}
+
+/// Orders directories first. Provides a fallback if inputs are not directories.
+fn dir_first_comparator(
+ a: &File,
+ b: &File,
+ fallback: impl Fn(&File, &File) -> Ordering,
+) -> Ordering {
+ match (a.is_dir(), b.is_dir()) {
+ (true, false) => Ordering::Greater,
+ (false, true) => Ordering::Less,
+ _ => fallback(a, b),
+ }
+}
+
+/// Orders directories last. Provides a fallback if inputs are not directories.
+fn dir_last_comparator(
+ a: &File,
+ b: &File,
+ fallback: impl Fn(&File, &File) -> Ordering,
+) -> Ordering {
+ match (a.is_dir(), b.is_dir()) {
+ (true, false) => Ordering::Less,
+ (false, true) => Ordering::Greater,
+ _ => fallback(a, b),
+ }
+}
+
+/// Grabs the comparator for two non-dir type [File]s.
+fn base_comparator(sort_type: Sort) -> Box<FileComparator> {
+ Box::new(match sort_type {
+ Sort::Name => naming::comparator,
+ Sort::Rname => naming::rev_comparator,
+ Sort::Size => sizing::comparator,
+ Sort::Rsize => sizing::rev_comparator,
+ Sort::Access => time_stamping::accessed::comparator,
+ Sort::Raccess => time_stamping::accessed::rev_comparator,
+ Sort::Create => time_stamping::created::comparator,
+ Sort::Rcreate => time_stamping::created::rev_comparator,
+ Sort::Mod => time_stamping::modified::comparator,
+ Sort::Rmod => time_stamping::modified::rev_comparator,
+
+ // Hacky...
+ Sort::None => unreachable!(),
+ })
+}
+
+mod time_stamping {
+ pub(self) use super::File;
+ pub(self) use core::cmp::Ordering;
+ pub(self) use std::time::SystemTime;
+
+ pub mod accessed {
+ use super::*;
+
+ /// Comparator that sorts [File]s by Last Access timestamp, newer to older.
+ pub fn comparator(a: &File, b: &File) -> Ordering {
+ let a_stamp = a.metadata().accessed().unwrap_or_else(|_| SystemTime::now());
+ let b_stamp = b.metadata().accessed().unwrap_or_else(|_| SystemTime::now());
+ b_stamp.cmp(&a_stamp)
+ }
+
+ /// Comparator that sorts [File]s by Access timestamp, older to newer.
+ pub fn rev_comparator(a: &File, b: &File) -> Ordering {
+ comparator(b, a)
+ }
+ }
+
+ pub mod created {
+ use super::*;
+
+ /// Comparator that sorts [File]s by Creation timestamp, newer to older.
+ pub fn comparator(a: &File, b: &File) -> Ordering {
+ let a_stamp = a.metadata().created().unwrap_or_else(|_| SystemTime::now());
+ let b_stamp = b.metadata().created().unwrap_or_else(|_| SystemTime::now());
+ b_stamp.cmp(&a_stamp)
+ }
+
+ /// Comparator that sorts [File]s by Creation timestamp, older to newer.
+ pub fn rev_comparator(a: &File, b: &File) -> Ordering {
+ comparator(b, a)
+ }
+ }
+
+ pub mod modified {
+ use super::*;
+
+ /// Comparator that sorts [File]s by Alteration timestamp, newer to older.
+ pub fn comparator(a: &File, b: &File) -> Ordering {
+ let a_stamp = a.metadata().modified().unwrap_or_else(|_| SystemTime::now());
+ let b_stamp = b.metadata().modified().unwrap_or_else(|_| SystemTime::now());
+ b_stamp.cmp(&a_stamp)
+ }
+
+ /// Comparator that sorts [File]s by Alteration timestamp, older to newer.
+ pub fn rev_comparator(a: &File, b: &File) -> Ordering {
+ comparator(b, a)
+ }
+ }
+}
+
+mod sizing {
+ use super::File;
+ use std::cmp::Ordering;
+
+ /// Comparator that sorts [File]s by size, largest to smallest.
+ pub fn comparator(a: &File, b: &File) -> Ordering {
+ let a_size = a.size().value();
+ let b_size = b.size().value();
+ b_size.cmp(&a_size)
+ }
+ /// Comparator that sorts [File]s by size, smallest to largest.
+ pub fn rev_comparator(a: &File, b: &File) -> Ordering {
+ comparator(b, a)
+ }
+}
+
+mod naming {
+ use super::File;
+ use std::cmp::Ordering;
+
+ /// Comparator based on [File] file names in lexicographical order.
+ pub fn comparator(a: &File, b: &File) -> Ordering {
+ a.file_name().cmp(b.file_name())
+ }
+
+ /// Comparator based on [File] file names in reversed lexicographical order.
+ pub fn rev_comparator(a: &File, b: &File) -> Ordering {
+ comparator(b, a)
+ }
+}
diff --git a/src/file/tree/mod.rs b/src/file/tree/mod.rs
index ccbda92..6a6888a 100644
--- a/src/file/tree/mod.rs
+++ b/src/file/tree/mod.rs
@@ -1,8 +1,9 @@
use crate::{
error::prelude::*,
file::File,
- user::{column, Context},
+ user::{args::Sort, column, Context},
};
+use super::order::{self, FileComparator};
use ahash::{HashMap, HashSet};
use indextree::{Arena, NodeId};
use std::{ops::Deref, path::PathBuf};
@@ -104,6 +105,10 @@ impl Tree {
column_metadata.update_size_width(arena[root_id].get(), ctx);
+ if let Some(comparator) = order::comparator(ctx) {
+ Self::tree_sort(root_id, &mut arena, comparator);
+ }
+
let tree = Self { root_id, arena };
Ok((tree, column_metadata))
@@ -134,15 +139,15 @@ impl Tree {
let child_node = arena[child_node_id].get();
- let inode = match child_node.inode() {
+ #[cfg(unix)]
+ match child_node.inode() {
Ok(value) => {
- #[cfg(unix)]
column_metadata.update_inode_attr_widths(&value);
value
},
Err(err) => {
log::warn!(
- "Failed to query inode of {}",
+ "Failed to query inode of {}: {err}",
child_node.path().display(),
);
continue;
@@ -158,6 +163,12 @@ impl Tree {
dfs_stack.pop();
}
+ if !matches!(ctx.sort, Sort::Size | Sort::Rsize) {
+ if let Some(comparator) = order::comparator(ctx) {
+ Self::tree_sort(root_id, &mut arena, comparator);
+ }
+ }
+
let tree = Self { root_id, arena };
Ok((tree, column_metadata))
@@ -210,10 +221,7 @@ impl Tree {
.root_id
.descendants(&self.arena)
.filter(|n| {
- self.arena[*n]
- .get()
- .file_type()
- .is_some_and(|ft| ft.is_dir() && n.children(&self.arena).count() == 0)
+ self.arena[*n].get().is_dir() && n.children(&self.arena).count() == 0
})
.collect::<Vec<_>>();
@@ -229,6 +237,12 @@ impl Tree {
pub fn arena(&self) -> &Arena<File> {
&self.arena
}
+
+ /// Sort [`File`]s in the `arena` with the provided `comparator`.
+ pub fn tree_sort(root_id: NodeId, arena: &mut Arena<File>, comparator: Box<FileComparator>) {
+ todo!()
+ }
+
}
impl Deref for Tree {
diff --git a/src/render/mod.rs b/src/render/mod.rs
index 01d689f..24a5358 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -58,7 +58,7 @@ fn tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
let node = arena[node_id].get();
let depth = node.depth();
- if utils::node_is_dir(node) {
+ if node.is_dir() {
inherited_prefix_components.pop();
}
@@ -72,7 +72,7 @@ fn tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
let node = arena[node_id].get();
let depth = node.depth();
- if utils::node_is_dir(node) {
+ if node.is_dir() {
if is_first_sibling(node_id, depth) {
inherited_prefix_components.push(SEP);
} else {
@@ -145,7 +145,7 @@ pub fn inverted_tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
let node = arena[node_id].get();
let depth = node.depth();
- if utils::node_is_dir(node) && depth < max_depth {
+ if node.is_dir() && depth < max_depth {
inherited_prefix_components.pop();
}
continue;
@@ -208,11 +208,3 @@ fn flat(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
Ok(buf)
}
-
-mod utils {
- use crate::file::File;
-
- pub fn node_is_dir(node: &File) -> bool {
- node.file_type().is_some_and(|ft| ft.is_dir())
- }
-}
diff --git a/src/user/args.rs b/src/user/args.rs
index 4c30fde..d4753bb 100644
--- a/src/user/args.rs
+++ b/src/user/args.rs
@@ -84,3 +84,61 @@ pub enum Layout {
/// Outputs a flat layout using paths rather than an ASCII tree
Flat,
}
+
+/// Order in which to print entries relative to their siblings (tree layouts) or all others (flat
+/// layout).
+#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord, Default)]
+pub enum Sort {
+ /// No ordering.
+ #[default]
+ None,
+
+ /// Sort entries by file name in lexicographical order
+ Name,
+ /// Sort entries by file name in reversed lexicographical order
+ Rname,
+
+ /// Sort entries by size smallest to largest, top to bottom
+ Size,
+
+ /// Sort entries by size largest to smallest, bottom to top
+ Rsize,
+
+ /// Sort entries by newer to older Accessing Date
+ #[value(alias("atime"))]
+ Access,
+
+ /// Sort entries by older to newer Accessing Date
+ #[value(alias("ratime"))]
+ Raccess,
+
+ /// Sort entries by newer to older Creation Date
+ #[value(alias("ctime"))]
+ Create,
+
+ /// Sort entries by older to newer Creation Date
+ #[value(alias("rctime"))]
+ Rcreate,
+
+ /// Sort entries by newer to older Alteration Date
+ #[value(alias("mtime"))]
+ Mod,
+
+ /// Sort entries by older to newer Alteration Date
+ #[value(alias("rmtime"))]
+ Rmod,
+}
+
+/// How directories should be ordered relative to regular files.
+#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, Default)]
+pub enum DirOrder {
+ /// No particular ordering for directories relative to other files
+ #[default]
+ None,
+
+ /// Sort directories above files
+ First,
+
+ /// Sort directories below files
+ Last,
+}
diff --git a/src/user/mod.rs b/src/user/mod.rs
index e34ff91..e88ad89 100644
--- a/src/user/mod.rs
+++ b/src/user/mod.rs
@@ -89,6 +89,14 @@ pub struct Context {
#[arg(short, long)]
pub prune: bool,
+ /// How to sort entries
+ #[arg(short, long, value_enum, default_value_t)]
+ pub sort: args::Sort,
+
+ /// Sort directories before or after all other file types
+ #[arg(short, long, value_enum, default_value_t)]
+ pub dir_order: args::DirOrder,
+
/// Which kind of layout to use when rendering the output
#[arg(short = 'y', long, value_enum, default_value_t)]
pub layout: args::Layout,