Skip to content

Commit

Permalink
Merge pull request #155 from KP64/master
Browse files Browse the repository at this point in the history
Unify Glob arguments into one Argument
  • Loading branch information
solidiquis authored May 1, 2023
2 parents 269940e + a86dc14 commit b6ce790
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 85 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ description = """
erdtree (erd) is a cross-platform multi-threaded filesystem and disk usage
analysis tool that respects gitignore and hidden file rules.
"""
categories = ["command-line-utilities"]
documentation = "https://github.com/solidiquis/erdtree"
homepage = "https://github.com/solidiquis/erdtree"
repository = "https://github.com/solidiquis/erdtree"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ To add extra granularity to how directories are sorted relative to other file-ty
```
--dir-order <DIR_ORDER>
Sort directories before or after all other file types
[default: none]
Possible values:
Expand Down
17 changes: 5 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
clippy::complexity,
clippy::perf,
clippy::pedantic,
clippy::nursery
clippy::nursery,
clippy::cargo
)]
#![allow(
clippy::struct_excessive_bools,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::cast_possible_truncation
)]

use clap::CommandFactory;
use render::{
context::Context,
Expand All @@ -23,7 +25,7 @@ use render::{
Tree,
},
};
use std::{io::stdout, process::ExitCode};
use std::{error::Error, io::stdout};

/// Operations to wrangle ANSI escaped strings.
mod ansi;
Expand All @@ -43,16 +45,7 @@ mod tty;
/// Common utilities across all modules.
mod utils;

fn main() -> ExitCode {
if let Err(e) = run() {
eprintln!("{e}");
return ExitCode::FAILURE;
}

ExitCode::SUCCESS
}

fn run() -> Result<(), Box<dyn std::error::Error>> {
fn main() -> Result<(), Box<dyn Error>> {
let ctx = Context::init()?;

if let Some(shell) = ctx.completions {
Expand Down
42 changes: 23 additions & 19 deletions src/render/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ mod test;

/// Defines the CLI.
#[derive(Parser, Debug)]
#[command(name = "erdtree")]
#[command(author = "Benjamin Nguyen. <[email protected]>")]
#[command(version = "2.0.0")]
#[command(about = "erdtree (erd) is a cross-platform multi-threaded filesystem and disk usage analysis tool.", long_about = None)]
#[command(name = env!("CARGO_PKG_NAME", "The Package Name is missing!"))]
#[command(author = env!("CARGO_PKG_AUTHORS", "The Author of the Package is missing!"))]
#[command(version = env!("CARGO_PKG_VERSION_MAJOR", "The Package version is missing!"))]
#[command(about = "erdtree (erd) is a cross-platform multi-threaded filesystem and disk usage analysis tool.",
long_about = env!("CARGO_PKG_DESCRIPTION", "The Long Package Description is missing!"))]
pub struct Context {
/// Directory to traverse; defaults to current working directory
dir: Option<PathBuf>,
Expand All @@ -60,7 +61,7 @@ pub struct Context {
pub color: Coloring,

/// Print physical or logical file size
#[arg(short, long, value_enum, default_value_t = DiskUsage::default())]
#[arg(short, long, value_enum, default_value_t)]
pub disk_usage: DiskUsage,

/// Follow symlinks
Expand Down Expand Up @@ -107,11 +108,10 @@ pub struct Context {
pub pattern: Option<String>,

/// Enables glob based searching
#[arg(long, requires = "pattern")]
#[arg(group = "searching", long, requires = "pattern")]
pub glob: bool,

/// Enables case-insensitive glob based searching
#[arg(long, requires = "pattern")]
#[arg(group = "searching", long, requires = "pattern")]
pub iglob: bool,

/// Restrict regex or glob search to a particular file-type
Expand All @@ -123,7 +123,7 @@ pub struct Context {
pub prune: bool,

/// Sort-order to display directory content
#[arg(short, long, value_enum, default_value_t = sort::Type::default())]
#[arg(short, long, value_enum, default_value_t)]
pub sort: sort::Type,

/// Sort directories before or after all other file types
Expand Down Expand Up @@ -208,6 +208,16 @@ pub struct Context {
#[clap(skip)]
pub window_width: Option<usize>,
}

trait AsVecOfStr {
fn as_vec_of_str(&self) -> Vec<&str>;
}
impl AsVecOfStr for ArgMatches {
fn as_vec_of_str(&self) -> Vec<&str> {
self.ids().map(Id::as_str).collect()
}
}

type Predicate = Result<Box<dyn Fn(&DirEntry) -> bool + Send + Sync + 'static>, Error>;

impl Context {
Expand Down Expand Up @@ -238,16 +248,13 @@ impl Context {
// user arguments.
let mut args = vec![OsString::from("--")];

let mut ids = user_args.ids().map(Id::as_str).collect::<Vec<&str>>();
let mut ids = user_args.as_vec_of_str();

ids.extend(config_args.ids().map(Id::as_str).collect::<Vec<&str>>());
ids.extend(config_args.as_vec_of_str());

ids = crate::utils::uniq(ids);

for id in ids {
if id == "Context" {
continue;
}
for id in ids.into_iter().filter(|&id| id != "Context") {
if id == "dir" {
if let Ok(Some(raw)) = user_args.try_get_raw(id) {
let raw_args = raw.map(OsStr::to_owned).collect::<Vec<OsString>>();
Expand Down Expand Up @@ -388,10 +395,7 @@ impl Context {

let mut negated_glob = false;

let overrides = if !self.glob && !self.iglob {
// Shouldn't really ever be hit but placing here as a safeguard.
return Err(Error::EmptyGlob);
} else {
let overrides = {
if self.iglob {
builder.case_insensitive(true)?;
}
Expand Down
20 changes: 18 additions & 2 deletions src/render/context/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,29 @@ use clap::ValueEnum;
/// Order in which to print nodes.
#[derive(Copy, Clone, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum Type {
/// Sort entries by file name
/// Sort entries by file name in lexicographical order.
Name,
/// Sort entries by file name in reversed lexicographical order.
NameRev,

/// Sort entries by size smallest to largest, top to bottom
#[default]
Size,

/// Sort entries by size largest to smallest, bottom to top
SizeRev,

/// Sort entries by newer to older Accessing Date
Access,
/// Sort entries by older to newer Accessing Date
AccessRev,

/// Sort entries by newer to older Creation Date
Creation,
/// Sort entries by older to newer Creation Date
CreationRev,

/// Sort entries by newer to older Alteration Date
Modification,
/// Sort entries by older to newer Alteration Date
ModificationRev,
}
6 changes: 4 additions & 2 deletions src/render/tree/display/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ use crate::render::{
tree::Node,
};

type Theme = Box<dyn FnMut(&Node) -> &'static ThemesMap>;

/// Returns a closure that retrieves the regular theme.
pub fn regular_theme_getter() -> Box<dyn FnMut(&Node) -> &'static ThemesMap> {
pub fn regular_theme_getter() -> Theme {
Box::new(|_node: &Node| styles::get_tree_theme().unwrap())
}

/// Returns a closure that can smartly determine when a symlink is being followed and when it is
/// not being followed. When a symlink is being followed, all of its descendents should have tree
/// branches that are colored differently.
pub fn link_theme_getter() -> Box<dyn FnMut(&Node) -> &'static ThemesMap> {
pub fn link_theme_getter() -> Theme {
let mut link_depth = None;

Box::new(move |node: &Node| {
Expand Down
33 changes: 13 additions & 20 deletions src/render/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,19 +282,14 @@ where

/// Function to remove empty directories.
fn prune_directories(root_id_id: NodeId, tree: &mut Arena<Node>) {
let mut to_prune = vec![];

for node_id in root_id_id.descendants(tree).skip(1) {
let node = tree[node_id].get();

if !node.is_dir() {
continue;
}

if node_id.children(tree).count() == 0 {
to_prune.push(node_id);
}
}
let to_prune = root_id_id
.descendants(tree)
.skip(1)
.map(|node_id| (node_id, tree[node_id].get()))
.filter(|(_, node)| node.is_dir())
.map(|(node_id, _)| node_id)
.filter(|node_id| node_id.children(tree).count() == 0)
.collect::<Vec<_>>();

if to_prune.is_empty() {
return;
Expand All @@ -309,13 +304,11 @@ where

/// Filter for only directories.
fn filter_directories(root_id: NodeId, tree: &mut Arena<Node>) {
let mut to_detach = vec![];

for descendant_id in root_id.descendants(tree).skip(1) {
if !tree[descendant_id].get().is_dir() {
to_detach.push(descendant_id);
}
}
let to_detach = root_id
.descendants(tree)
.skip(1)
.filter(|&descendant_id| !tree[descendant_id].get().is_dir())
.collect::<Vec<_>>();

for descendant_id in to_detach {
descendant_id.detach(tree);
Expand Down
122 changes: 100 additions & 22 deletions src/render/tree/node/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@ pub fn comparator(ctx: &Context) -> Box<NodeComparator> {
base_comparator(sort_type)
}

/// Grabs the comparator for two non-dir type [Node]s.
fn base_comparator(sort_type: sort::Type) -> Box<NodeComparator> {
match sort_type {
sort::Type::Name => Box::new(name_comparator),
sort::Type::Size => Box::new(size_comparator),
sort::Type::SizeRev => Box::new(size_rev_comparator),
}
}

/// Orders directories first. Provides a fallback if inputs are not directories.
fn dir_first_comparator(
a: &Node,
Expand All @@ -57,22 +48,109 @@ fn dir_last_comparator(
}
}

/// Comparator that sorts [Node]s by size, smallest to largest.
fn size_rev_comparator(a: &Node, b: &Node) -> Ordering {
let a_size = a.file_size().map_or(0, |fs| fs.bytes);
let b_size = b.file_size().map_or(0, |fs| fs.bytes);
/// Grabs the comparator for two non-dir type [Node]s.
fn base_comparator(sort_type: sort::Type) -> Box<NodeComparator> {
Box::new(match sort_type {
sort::Type::Name => naming::comparator,
sort::Type::NameRev => naming::rev_comparator,

sort::Type::Size => sizing::comparator,
sort::Type::SizeRev => sizing::rev_comparator,

sort::Type::Access => time_stamping::accessed::comparator,
sort::Type::AccessRev => time_stamping::accessed::rev_comparator,

sort::Type::Creation => time_stamping::created::comparator,
sort::Type::CreationRev => time_stamping::created::rev_comparator,

sort::Type::Modification => time_stamping::modified::comparator,
sort::Type::ModificationRev => time_stamping::modified::rev_comparator,
})
}

mod time_stamping {
pub mod accessed {
use crate::render::tree::node::Node;
use core::cmp::Ordering;
use std::time::SystemTime;

/// Comparator that sorts [Node]s by Last Access timestamp, newer to older.
pub fn comparator(a: &Node, b: &Node) -> Ordering {
let a_stamp = a.accessed().unwrap_or_else(SystemTime::now);
let b_stamp = b.accessed().unwrap_or_else(SystemTime::now);
a_stamp.cmp(&b_stamp)
}

/// Comparator that sorts [Node]s by Access timestamp, older to newer.
pub fn rev_comparator(a: &Node, b: &Node) -> Ordering {
comparator(b, a)
}
}

pub mod created {
use crate::render::tree::node::Node;
use core::cmp::Ordering;
use std::time::SystemTime;

/// Comparator that sorts [Node]s by Creation timestamp, newer to older.
pub fn comparator(a: &Node, b: &Node) -> Ordering {
let a_stamp = a.created().unwrap_or_else(SystemTime::now);
let b_stamp = b.created().unwrap_or_else(SystemTime::now);
a_stamp.cmp(&b_stamp)
}

/// Comparator that sorts [Node]s by Creation timestamp, older to newer.
pub fn rev_comparator(a: &Node, b: &Node) -> Ordering {
comparator(b, a)
}
}

pub mod modified {
use crate::render::tree::node::Node;
use core::cmp::Ordering;
use std::time::SystemTime;

/// Comparator that sorts [Node]s by Alteration timestamp, newer to older.
pub fn comparator(a: &Node, b: &Node) -> Ordering {
let a_stamp = a.modified().unwrap_or_else(SystemTime::now);
let b_stamp = b.modified().unwrap_or_else(SystemTime::now);
a_stamp.cmp(&b_stamp)
}

a_size.cmp(&b_size)
/// Comparator that sorts [Node]s by Alteration timestamp, older to newer.
pub fn rev_comparator(a: &Node, b: &Node) -> Ordering {
comparator(b, a)
}
}
}

/// Comparator that sorts [Node]s by size, largest to smallest.
fn size_comparator(a: &Node, b: &Node) -> Ordering {
let a_size = a.file_size().map_or(0, |fs| fs.bytes);
let b_size = b.file_size().map_or(0, |fs| fs.bytes);
b_size.cmp(&a_size)
mod sizing {
use crate::render::tree::node::Node;
use core::cmp::Ordering;

/// Comparator that sorts [Node]s by size, largest to smallest.
pub fn comparator(a: &Node, b: &Node) -> Ordering {
let a_size = a.file_size().map_or(0, |fs| fs.bytes);
let b_size = b.file_size().map_or(0, |fs| fs.bytes);
b_size.cmp(&a_size)
}
/// Comparator that sorts [Node]s by size, smallest to largest.
pub fn rev_comparator(a: &Node, b: &Node) -> Ordering {
comparator(b, a)
}
}

/// Comparator based on [Node] file names.
fn name_comparator(a: &Node, b: &Node) -> Ordering {
a.file_name().cmp(b.file_name())
mod naming {
use crate::render::tree::node::Node;
use core::cmp::Ordering;

/// Comparator based on [Node] file names in lexicographical order.
pub fn comparator(a: &Node, b: &Node) -> Ordering {
a.file_name().cmp(b.file_name())
}

/// Comparator based on [Node] file names in reversed lexicographical order.
pub fn rev_comparator(a: &Node, b: &Node) -> Ordering {
comparator(b, a)
}
}
Loading

0 comments on commit b6ce790

Please sign in to comment.