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

Add option to print size on the left #61

Merged
merged 5 commits into from
Mar 14, 2023
Merged
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
4 changes: 4 additions & 0 deletions src/render/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ pub struct Context {
#[arg(long)]
pub suppress_size: bool,

/// Show the size on the left, decimal aligned
#[arg(long)]
pub size_left: bool,

/// Don't read configuration file
#[arg(long)]
pub no_config: bool,
Expand Down
198 changes: 120 additions & 78 deletions src/render/disk_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use std::{
path::Path,
};

use crate::Context;

/// Determines between logical or physical size for display
#[derive(Copy, Clone, Debug, ValueEnum)]
pub enum DiskUsage {
Expand Down Expand Up @@ -94,108 +96,148 @@ impl AddAssign<&Self> for FileSize {
}
}

impl Display for FileSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl FileSize {
/// Transforms the `FileSize` into a string.
/// `Display` / `ToString` traits not used in order to have control over alignment.
///
/// `align` false makes strings such as
/// `123.45 KiB`
/// `1.23 MiB`
/// `12 B`
///
/// `align` true makes strings such as
/// `123.45 KiB`
/// ` 1.23 MiB`
/// ` 12 B`
pub fn format(&self, align: bool) -> String {
bryceberger marked this conversation as resolved.
Show resolved Hide resolved
let fbytes = self.bytes as f64;

let output = match self.prefix_kind {
let scale = self.scale;
let (color, bytes, base) = match self.prefix_kind {
PrefixKind::Bin => {
let log = fbytes.log(2.0);

if log < 10.0 {
Color::Cyan.paint(format!("{} {}", self.bytes, BinPrefix::Base))
} else if (10.0..20.0).contains(&log) {
Color::Yellow.paint(format!(
"{:.scale$} {}",
fbytes / 1024.0_f64,
BinPrefix::Kibi,
scale = self.scale
))
} else if (20.0..30.0).contains(&log) {
Color::Green.paint(format!(
"{:.scale$} {}",
fbytes / 1024.0_f64.powi(2),
BinPrefix::Mebi,
scale = self.scale
))
} else if (30.0..40.0).contains(&log) {
Color::Red.paint(format!(
"{:.scale$} {}",
fbytes / 1024.0_f64.powi(3),
BinPrefix::Gibi,
scale = self.scale
))
let log = fbytes.log2();
if log < 10. {
(
Color::Cyan,
format!("{}", self.bytes),
format!("{}", BinPrefix::Base),
)
} else if log < 20. {
(
Color::Yellow,
format!("{:.scale$}", fbytes / 1024.0_f64.powi(1),),
format!("{}", BinPrefix::Kibi),
)
} else if log < 30. {
(
Color::Green,
format!("{:.scale$}", fbytes / 1024.0_f64.powi(2),),
format!("{}", BinPrefix::Mebi),
)
} else if log < 40. {
(
Color::Red,
format!("{:.scale$}", fbytes / 1024.0_f64.powi(3),),
format!("{}", BinPrefix::Gibi),
)
} else {
Color::Blue.paint(format!(
"{:.scale$} {}",
fbytes / 1024.0_f64.powi(4),
BinPrefix::Tebi,
scale = self.scale
))
(
Color::Blue,
format!("{:.scale$}", fbytes / 1024.0_f64.powi(4),),
format!("{}", BinPrefix::Tebi),
)
}
}

PrefixKind::Si => {
let log = fbytes.log(10.0);

if log < 3.0 {
Color::Cyan.paint(format!("{} {}", fbytes, SiPrefix::Base))
} else if (3.0..6.0).contains(&log) {
Color::Yellow.paint(format!(
"{:.scale$} {}",
fbytes / 10.0_f64.powi(3),
SiPrefix::Kilo,
scale = self.scale
))
} else if (6.0..9.0).contains(&log) {
Color::Green.paint(format!(
"{:.scale$} {}",
fbytes / 10.0_f64.powi(6),
SiPrefix::Mega,
scale = self.scale
))
} else if (9.0..12.0).contains(&log) {
Color::Green.paint(format!(
"{:.scale$} {}",
fbytes / 10.0_f64.powi(9),
SiPrefix::Giga,
scale = self.scale
))
let log = fbytes.log10();
if log < 3. {
(
Color::Cyan,
format!("{}", self.bytes),
format!("{}", SiPrefix::Base),
)
} else if log < 6. {
(
Color::Yellow,
format!("{:.scale$}", fbytes / 10.0_f64.powi(3),),
format!("{}", SiPrefix::Kilo),
)
} else if log < 9. {
(
Color::Green,
format!("{:.scale$}", fbytes / 10.0_f64.powi(6),),
format!("{}", SiPrefix::Mega),
)
} else if log < 12. {
(
Color::Red,
format!("{:.scale$}", fbytes / 10.0_f64.powi(9),),
format!("{}", SiPrefix::Giga),
)
} else {
Color::Red.paint(format!(
"{:.scale$} {}",
fbytes / 10.0_f64.powi(12),
SiPrefix::Tera,
scale = self.scale
))
(
Color::Blue,
format!("{:.scale$}", fbytes / 10.0_f64.powi(12),),
format!("{}", SiPrefix::Tera),
)
}
}
};
if align {
match self.prefix_kind {
PrefixKind::Bin => color
.paint(format!("{bytes:>len$} {base:>3}", len = self.scale + 4))
.to_string(),
PrefixKind::Si => color
.paint(format!("{bytes:>len$} {base:>2}", len = self.scale + 4))
.to_string(),
}
} else {
color.paint(format!("{bytes} {base}")).to_string()
}
}

write!(f, "{output}")
/// Returns spaces times the length of a file size, formatted with the given options
/// " " * len(123.45 KiB)
pub fn empty_string(ctx: &Context) -> String {
format!("{:len$}", "", len = Self::empty_string_len(ctx))
}

fn empty_string_len(ctx: &Context) -> usize {
// 3 places before the decimal
// 1 for the decimal
// ctx.scale after the decimal
// 1 space before unit
// 2/3 spaces per unit, depending
3 + 1
+ ctx.scale
+ 1
+ match ctx.prefix {
PrefixKind::Bin => 3,
PrefixKind::Si => 2,
}
}
}

impl Display for BinPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base => write!(f, "B"),
Self::Kibi => write!(f, "KiB"),
Self::Mebi => write!(f, "MiB"),
Self::Gibi => write!(f, "GiB"),
Self::Tebi => write!(f, "TiB"),
Self::Base => f.pad("B"),
Self::Kibi => f.pad("KiB"),
Self::Mebi => f.pad("MiB"),
Self::Gibi => f.pad("GiB"),
Self::Tebi => f.pad("TiB"),
}
}
}

impl Display for SiPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Base => write!(f, "B"),
Self::Kilo => write!(f, "KB"),
Self::Mega => write!(f, "MB"),
Self::Giga => write!(f, "GB"),
Self::Tera => write!(f, "TB"),
Self::Base => f.pad("B"),
Self::Kilo => f.pad("KB"),
Self::Mega => f.pad("MB"),
Self::Giga => f.pad("GB"),
Self::Tera => f.pad("TB"),
}
}
}
103 changes: 50 additions & 53 deletions src/render/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,7 @@ impl Tree {
fn assemble_tree(current_node: &mut Node, branches: &mut Branches, ctx: &Context) {
let children = branches.remove(current_node.path()).unwrap();

if children.len() > 0 {
current_node.set_children(children);
}
current_node.set_children(children);

let mut dir_size = FileSize::new(0, ctx.disk_usage, ctx.prefix, ctx.scale);

Expand All @@ -169,9 +167,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));
if let Some(order) = ctx.sort().map(|s| Order::from((s, ctx.dirs_first()))) {
if let Some(func) = order.comparator() {
current_node.sort_children(func);
}
}
}
}
Expand Down Expand Up @@ -199,74 +198,72 @@ impl Display for Tree {
let root = self.root();
let level = self.level();
let theme = ui::get_theme();
let prune = self.context().prune;
let mut output = String::from("");

#[inline]
fn extend_output(output: &mut String, node: &Node, prefix: &str) {
output.push_str(&format!("{prefix}{node}\n"));
let ctx = self.context();
fn extend_output(
bryceberger marked this conversation as resolved.
Show resolved Hide resolved
f: &mut Formatter,
node: &Node,
prefix: &str,
ctx: &Context,
) -> fmt::Result {
if ctx.size_left && !ctx.suppress_size {
node.display_size_left(f, prefix, ctx)?;
writeln!(f, "")
} else {
node.display_size_right(f, prefix, ctx)?;
writeln!(f, "")
}
}

#[inline]
fn traverse(
bryceberger marked this conversation as resolved.
Show resolved Hide resolved
output: &mut String,
f: &mut Formatter,
children: Iter<Node>,
base_prefix: &str,
level: usize,
theme: &ui::ThemesMap,
prune: bool,
) {
ctx: &Context,
) -> fmt::Result {
let mut peekable = children.peekable();

loop {
if let Some(child) = peekable.next() {
let last_entry = peekable.peek().is_none();

let mut prefix = base_prefix.to_owned();

if last_entry {
prefix.push_str(theme.get("uprt").unwrap());
while let Some(child) = peekable.next() {
let last_entry = peekable.peek().is_none();
let prefix = base_prefix.to_owned()
+ if last_entry {
theme.get("uprt").unwrap()
} else {
prefix.push_str(theme.get("vtrt").unwrap());
}
theme.get("vtrt").unwrap()
};

extend_output(output, child, &prefix);
extend_output(f, child, &prefix, ctx)?;

if !child.is_dir() || child.depth + 1 > level {
continue;
}

if child.has_children() {
let children = child.children();
if !child.is_dir() || child.depth + 1 > level {
continue;
}

let mut new_base = base_prefix.to_owned();
if child.has_children() {
let children = child.children();

let new_theme = child
.is_symlink()
.then(|| ui::get_link_theme())
.unwrap_or(theme);
let new_theme = if child.is_symlink() {
ui::get_link_theme()
} else {
theme
};

if last_entry {
new_base.push_str(ui::SEP);
let new_base = base_prefix.to_owned()
+ if last_entry {
ui::SEP
} else {
new_base.push_str(theme.get("vt").unwrap());
}

traverse(output, children, &new_base, level, new_theme, prune);
}
theme.get("vt").unwrap()
};

continue;
traverse(f, children, &new_base, level, new_theme, ctx)?;
}
break;
}
Ok(())
}

extend_output(&mut output, root, "");

if root.has_children() {
traverse(&mut output, root.children(), "", level, theme, prune)
}

write!(f, "{output}")
extend_output(f, root, "", ctx)?;
traverse(f, root.children(), "", level, theme, ctx)?;
Ok(())
}
}
Loading