From c0a2cf50afc71b0845ded807325b2c5966688739 Mon Sep 17 00:00:00 2001 From: Benjamin Sago Date: Mon, 3 Jul 2017 22:46:40 +0100 Subject: [PATCH] Encapsulate tree depth It only really gets used for zeroes and having one added to it. --- src/output/details.rs | 22 +++++----- src/output/grid_details.rs | 4 +- src/output/tree.rs | 84 ++++++++++++++++++++++---------------- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/src/output/details.rs b/src/output/details.rs index 48fc17b..2eb8ee4 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -70,7 +70,7 @@ use options::{FileFilter, RecurseOptions}; use output::colours::Colours; use output::column::Columns; use output::cell::TextCell; -use output::tree::{TreeTrunk, TreeParams}; +use output::tree::{TreeTrunk, TreeParams, TreeDepth}; use output::file_name::{FileName, LinkStyle, Classify}; use output::table::{Table, Environment, Row as TableRow}; @@ -153,14 +153,14 @@ impl<'a> Render<'a> { // This is weird, but I can't find a way around it: // https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6 let mut table = Some(table); - self.add_files_to_table(&mut table, &mut rows, &self.files, 0); + self.add_files_to_table(&mut table, &mut rows, &self.files, TreeDepth(0)); for row in self.iterate_with_table(table.unwrap(), rows) { writeln!(w, "{}", row.strings())? } } else { - self.add_files_to_table(&mut None, &mut rows, &self.files, 0); + self.add_files_to_table(&mut None, &mut rows, &self.files, TreeDepth(0)); for row in self.iterate(rows) { writeln!(w, "{}", row.strings())? @@ -172,7 +172,7 @@ impl<'a> Render<'a> { /// Adds files to the table, possibly recursively. This is easily /// parallelisable, and uses a pool of threads. - fn add_files_to_table<'dir>(&self, table: &mut Option>, rows: &mut Vec, src: &Vec>, depth: usize) { + fn add_files_to_table<'dir>(&self, table: &mut Option>, rows: &mut Vec, src: &Vec>, depth: TreeDepth) { use num_cpus; use scoped_threadpool::Pool; use std::sync::{Arc, Mutex}; @@ -208,7 +208,7 @@ impl<'a> Render<'a> { let mut dir = None; if let Some(r) = self.recurse { - if file.is_directory() && r.tree && !r.is_too_deep(depth) { + if file.is_directory() && r.tree && !r.is_too_deep(depth.0) { if let Ok(d) = file.to_dir(false) { dir = Some(d); } @@ -252,33 +252,33 @@ impl<'a> Render<'a> { if !files.is_empty() { for xattr in egg.xattrs { - rows.push(self.render_xattr(xattr, TreeParams::new(depth + 1, false))); + rows.push(self.render_xattr(xattr, TreeParams::new(depth.deeper(), false))); } for (error, path) in errors { - rows.push(self.render_error(&error, TreeParams::new(depth + 1, false), path)); + rows.push(self.render_error(&error, TreeParams::new(depth.deeper(), false), path)); } - self.add_files_to_table(table, rows, &files, depth + 1); + self.add_files_to_table(table, rows, &files, depth.deeper()); continue; } } let count = egg.xattrs.len(); for (index, xattr) in egg.xattrs.into_iter().enumerate() { - rows.push(self.render_xattr(xattr, TreeParams::new(depth + 1, errors.is_empty() && index == count - 1))); + rows.push(self.render_xattr(xattr, TreeParams::new(depth.deeper(), errors.is_empty() && index == count - 1))); } let count = errors.len(); for (index, (error, path)) in errors.into_iter().enumerate() { - rows.push(self.render_error(&error, TreeParams::new(depth + 1, index == count - 1), path)); + rows.push(self.render_error(&error, TreeParams::new(depth.deeper(), index == count - 1), path)); } } } pub fn render_header(&self, header: TableRow) -> Row { Row { - tree: TreeParams::new(0, false), + tree: TreeParams::new(TreeDepth(0), false), cells: Some(header), name: TextCell::paint_str(self.colours.header, "Name"), } diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index 01ed1d9..1ea0da4 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -14,7 +14,7 @@ use output::details::{Options as DetailsOptions, Row as DetailsRow, Render as De use output::grid::Options as GridOptions; use output::file_name::{FileName, LinkStyle, Classify}; use output::table::{Table, Environment, Row as TableRow}; -use output::tree::TreeParams; +use output::tree::{TreeParams, TreeDepth}; pub struct Render<'a> { @@ -120,7 +120,7 @@ impl<'a> Render<'a> { let (ref mut table, ref mut rows) = tables[index]; table.add_widths(&row); - let details_row = drender.render_file(row, file_name.clone(), TreeParams::new(0, false)); + let details_row = drender.render_file(row, file_name.clone(), TreeParams::new(TreeDepth(0), false)); rows.push(details_row); } diff --git a/src/output/tree.rs b/src/output/tree.rs index b996ad9..2d8b124 100644 --- a/src/output/tree.rs +++ b/src/output/tree.rs @@ -88,12 +88,15 @@ pub struct TreeParams { /// How many directories deep into the tree structure this is. Directories /// on top have depth 0. - depth: usize, + depth: TreeDepth, /// Whether this is the last entry in the directory. last: bool, } +#[derive(Debug, Copy, Clone)] +pub struct TreeDepth(pub usize); + impl TreeTrunk { /// Calculates the tree parts for an entry at the given depth and @@ -108,40 +111,45 @@ impl TreeTrunk { // If this isn’t our first iteration, then update the tree parts thus // far to account for there being another row after it. if let Some(last) = self.last_params { - self.stack[last.depth] = if last.last { TreePart::Blank } else { TreePart::Line }; + self.stack[last.depth.0] = if last.last { TreePart::Blank } else { TreePart::Line }; } // Make sure the stack has enough space, then add or modify another // part into it. - self.stack.resize(params.depth + 1, TreePart::Edge); - self.stack[params.depth] = if params.last { TreePart::Corner } else { TreePart::Edge }; + self.stack.resize(params.depth.0 + 1, TreePart::Edge); + self.stack[params.depth.0] = if params.last { TreePart::Corner } else { TreePart::Edge }; self.last_params = Some(params); // Return the tree parts as a slice of the stack. // - // Ignoring the first component is specific to exa: when a user prints - // a tree view for multiple directories, we don’t want there to be a - // ‘zeroth level’ connecting the initial directories. Otherwise, not - // only are unrelated directories seemingly connected to each other, - // but the tree part of the first row doesn’t connect to anything: + // Ignore the first element here to prevent a 'zeroth level' from + // appearing before the very first directory. This level would + // join unrelated directories without connecting to anything: + // + // with [0..] with [1..] + // ========== ========== + // ├── folder folder + // │ └── file └── file + // └── folder folder + // └── file └──file // - // with [0..] with [1..] - // ========== ========== - // ├──folder folder - // │ └──file └──file - // └──folder folder - // └──file └──file &self.stack[1..] } } impl TreeParams { - pub fn new(depth: usize, last: bool) -> TreeParams { + pub fn new(depth: TreeDepth, last: bool) -> TreeParams { TreeParams { depth, last } } pub fn is_zero(&self) -> bool { - self.depth == 0 + self.depth.0 == 0 + } +} + +impl TreeDepth { + pub fn deeper(self) -> TreeDepth { + TreeDepth(self.0 + 1) } } @@ -150,50 +158,54 @@ impl TreeParams { mod test { use super::*; + fn params(depth: usize, last: bool) -> TreeParams { + TreeParams::new(TreeDepth(depth), last) + } + #[test] fn empty_at_first() { let mut tt = TreeTrunk::default(); - assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); + assert_eq!(tt.new_row(params(0, true)), &[]); } #[test] fn one_child() { let mut tt = TreeTrunk::default(); - assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); - assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); + assert_eq!(tt.new_row(params(0, true)), &[]); + assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]); } #[test] fn two_children() { let mut tt = TreeTrunk::default(); - assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); - assert_eq!(tt.new_row(TreeParams::new(1, false)), &[ TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); + assert_eq!(tt.new_row(params(0, true)), &[]); + assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]); + assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]); } #[test] fn two_times_two_children() { let mut tt = TreeTrunk::default(); - assert_eq!(tt.new_row(TreeParams::new(0, false)), &[]); - assert_eq!(tt.new_row(TreeParams::new(1, false)), &[ TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); + assert_eq!(tt.new_row(params(0, false)), &[]); + assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]); + assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]); - assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); - assert_eq!(tt.new_row(TreeParams::new(1, false)), &[ TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); + assert_eq!(tt.new_row(params(0, true)), &[]); + assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]); + assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]); } #[test] fn two_times_two_nested_children() { let mut tt = TreeTrunk::default(); - assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); + assert_eq!(tt.new_row(params(0, true)), &[]); - assert_eq!(tt.new_row(TreeParams::new(1, false)), &[ TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(2, false)), &[ TreePart::Line, TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(2, true)), &[ TreePart::Line, TreePart::Corner ]); + assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]); + assert_eq!(tt.new_row(params(2, false)), &[ TreePart::Line, TreePart::Edge ]); + assert_eq!(tt.new_row(params(2, true)), &[ TreePart::Line, TreePart::Corner ]); - assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); - assert_eq!(tt.new_row(TreeParams::new(2, false)), &[ TreePart::Blank, TreePart::Edge ]); - assert_eq!(tt.new_row(TreeParams::new(2, true)), &[ TreePart::Blank, TreePart::Corner ]); + assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]); + assert_eq!(tt.new_row(params(2, false)), &[ TreePart::Blank, TreePart::Edge ]); + assert_eq!(tt.new_row(params(2, true)), &[ TreePart::Blank, TreePart::Corner ]); } }