summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Nguyen <benjamin.van.nguyen@gmail.com>2023-11-26 17:37:26 -0800
committerBenjamin Nguyen <benjamin.van.nguyen@gmail.com>2023-11-26 17:37:26 -0800
commitb2efcf666b6b7ce3fbedbe45ab93c9201877de34 (patch)
treed85be1e34f4e8fb017bd4eee3bf001c8a3f03157
parent8fa8f72ca6289695515f497c08ba1b6125cb5bce (diff)
inverted tree
-rw-r--r--src/main.rs2
-rw-r--r--src/render/mod.rs105
-rw-r--r--src/render/row/mod.rs13
3 files changed, 110 insertions, 10 deletions
diff --git a/src/main.rs b/src/main.rs
index 6691d3a..1dc860a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -45,7 +45,7 @@ fn run() -> Result<()> {
Ok(tree)
})?;
- let output = render::tree(&file_tree, &ctx)?;
+ let output = render::output(&file_tree, &ctx)?;
let mut stdout = stdout().lock();
writeln!(stdout, "{output}").into_report(ErrorCategory::Warning)?;
diff --git a/src/render/mod.rs b/src/render/mod.rs
index f816da4..0a5863c 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -1,4 +1,4 @@
-use crate::{error::prelude::*, file, user::Context};
+use crate::{error::prelude::*, file, user::{args::Layout, Context}};
use indextree::{NodeEdge, NodeId};
/// Used for padding between tree branches.
@@ -20,7 +20,22 @@ pub const ROTATED_T: &str = "\u{251C}\u{2500} ";
/// row in the program output.
mod row;
-pub fn tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("Root node could not be computed")]
+ MissingRoot
+}
+
+pub fn output(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
+ match ctx.layout {
+ Layout::Regular => tree(file_tree, ctx),
+ Layout::Inverted => inverted_tree(file_tree, ctx),
+ Layout::Flat => todo!(),
+ Layout::Iflat => todo!(),
+ }
+}
+
+fn tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
let arena = file_tree.arena();
let root = file_tree.root_id();
let max_depth = ctx.level();
@@ -94,6 +109,92 @@ pub fn tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
Ok(buf)
}
+pub fn inverted_tree(file_tree: &file::Tree, ctx: &Context) -> Result<String> {
+ let arena = file_tree.arena();
+ let root = file_tree.root_id();
+ let max_depth = ctx.level();
+
+ let mut buf = String::new();
+
+ let is_last_sibling = |node_id: NodeId, depth: usize| {
+ (depth > 0)
+ .then(|| node_id.following_siblings(arena).skip(1).next().is_none())
+ .unwrap_or(false)
+ };
+
+ let mut inherited_prefix_components = vec![""];
+
+ let mut formatter = row::formatter(&mut buf, ctx);
+
+ let mut traverse = root.traverse(arena);
+
+ match traverse
+ .next()
+ .ok_or(Error::MissingRoot)
+ .into_report(ErrorCategory::Internal)
+ .context(error_source!())?
+ {
+ NodeEdge::Start(root_id) => {
+ formatter(arena[root_id].get(), "".to_string())
+ .into_report(ErrorCategory::Internal)
+ .context(error_source!())?;
+ }
+ _ => unreachable!()
+ }
+
+ for node_edge in traverse {
+ let (node, node_id, depth) = match node_edge {
+ NodeEdge::Start(node_id) => {
+ let node = arena[node_id].get();
+ let depth = node.depth();
+
+ if depth > max_depth {
+ continue;
+ }
+
+ (node, node_id, depth)
+ }
+ NodeEdge::End(node_id) => {
+ let node = arena[node_id].get();
+ let depth = node.depth();
+
+ if utils::node_is_dir(&node) && depth < max_depth {
+ inherited_prefix_components.pop();
+ }
+ continue;
+ }
+ };
+
+ let prefix = format!(
+ "{}{}",
+ inherited_prefix_components.join(""),
+ (depth > 0)
+ .then(|| {
+ is_last_sibling(node_id, depth)
+ .then_some(BL_CORNER)
+ .unwrap_or(ROTATED_T)
+ })
+ .unwrap_or("")
+ );
+
+ if let Err(e) = formatter(&node, prefix) {
+ log::warn!("{e}");
+ }
+
+ if utils::node_is_dir(&node) && depth < max_depth {
+ if is_last_sibling(node_id, depth) {
+ inherited_prefix_components.push(SEP);
+ } else {
+ inherited_prefix_components.push(VLINE);
+ }
+ }
+ }
+
+ drop(formatter);
+
+ Ok(buf)
+}
+
mod utils {
use crate::file::File;
diff --git a/src/render/row/mod.rs b/src/render/row/mod.rs
index b035906..ddeac87 100644
--- a/src/render/row/mod.rs
+++ b/src/render/row/mod.rs
@@ -1,5 +1,5 @@
-use crate::{error::prelude::*, file::File, user::{Context, column}};
-use std::fmt::Write;
+use crate::{file::File, user::{Context, column}};
+use std::fmt::{self, Write};
/// Concerned with how to present long-format for a particular file.
#[cfg(unix)]
@@ -9,14 +9,14 @@ mod long;
pub fn formatter<'a>(
buf: &'a mut String,
ctx: &'a Context,
-) -> Box<dyn FnMut(&File, String) -> Result<()> + 'a> {
+) -> Box<dyn FnMut(&File, String) -> fmt::Result + 'a> {
Box::new(|file, prefix| {
let size = format!("{}", file.size());
let name = file.file_name().to_string_lossy();
let column::Widths {
size: size_width, ..
} = ctx.col_widths();
- writeln!(buf, "{size:>size_width$} {prefix}{name}").into_report(ErrorCategory::Warning)
+ writeln!(buf, "{size:>size_width$} {prefix}{name}")
})
}
@@ -24,7 +24,7 @@ pub fn formatter<'a>(
pub fn formatter<'a>(
buf: &'a mut String,
ctx: &'a Context,
-) -> Box<dyn FnMut(&File, String) -> Result<()> + 'a> {
+) -> Box<dyn FnMut(&File, String) -> fmt::Result + 'a> {
if !ctx.long {
return Box::new(|file, prefix| {
let size = format!("{}", file.size());
@@ -32,7 +32,7 @@ pub fn formatter<'a>(
let column::Metadata {
max_size_width, ..
} = ctx.column_metadata;
- writeln!(buf, "{size:>max_size_width$} {prefix}{name}").into_report(ErrorCategory::Warning)
+ writeln!(buf, "{size:>max_size_width$} {prefix}{name}")
});
}
@@ -44,6 +44,5 @@ pub fn formatter<'a>(
let long_format = long::Format::new(file, ctx);
writeln!(buf, "{long_format} {size:>max_size_width$} {prefix}{name}")
- .into_report(ErrorCategory::Warning)
})
}