Encapsulate tree depth

It only really gets used for zeroes and having one added to it.
This commit is contained in:
Benjamin Sago 2017-07-03 22:46:40 +01:00
parent 8453f45f99
commit c0a2cf50af
3 changed files with 61 additions and 49 deletions

View File

@ -70,7 +70,7 @@ use options::{FileFilter, RecurseOptions};
use output::colours::Colours; use output::colours::Colours;
use output::column::Columns; use output::column::Columns;
use output::cell::TextCell; use output::cell::TextCell;
use output::tree::{TreeTrunk, TreeParams}; use output::tree::{TreeTrunk, TreeParams, TreeDepth};
use output::file_name::{FileName, LinkStyle, Classify}; use output::file_name::{FileName, LinkStyle, Classify};
use output::table::{Table, Environment, Row as TableRow}; 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: // 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 // https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6
let mut table = Some(table); 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) { for row in self.iterate_with_table(table.unwrap(), rows) {
writeln!(w, "{}", row.strings())? writeln!(w, "{}", row.strings())?
} }
} }
else { 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) { for row in self.iterate(rows) {
writeln!(w, "{}", row.strings())? writeln!(w, "{}", row.strings())?
@ -172,7 +172,7 @@ impl<'a> Render<'a> {
/// Adds files to the table, possibly recursively. This is easily /// Adds files to the table, possibly recursively. This is easily
/// parallelisable, and uses a pool of threads. /// parallelisable, and uses a pool of threads.
fn add_files_to_table<'dir>(&self, table: &mut Option<Table<'a>>, rows: &mut Vec<Row>, src: &Vec<File<'dir>>, depth: usize) { fn add_files_to_table<'dir>(&self, table: &mut Option<Table<'a>>, rows: &mut Vec<Row>, src: &Vec<File<'dir>>, depth: TreeDepth) {
use num_cpus; use num_cpus;
use scoped_threadpool::Pool; use scoped_threadpool::Pool;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -208,7 +208,7 @@ impl<'a> Render<'a> {
let mut dir = None; let mut dir = None;
if let Some(r) = self.recurse { 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) { if let Ok(d) = file.to_dir(false) {
dir = Some(d); dir = Some(d);
} }
@ -252,33 +252,33 @@ impl<'a> Render<'a> {
if !files.is_empty() { if !files.is_empty() {
for xattr in egg.xattrs { 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 { 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; continue;
} }
} }
let count = egg.xattrs.len(); let count = egg.xattrs.len();
for (index, xattr) in egg.xattrs.into_iter().enumerate() { 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(); let count = errors.len();
for (index, (error, path)) in errors.into_iter().enumerate() { 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 { pub fn render_header(&self, header: TableRow) -> Row {
Row { Row {
tree: TreeParams::new(0, false), tree: TreeParams::new(TreeDepth(0), false),
cells: Some(header), cells: Some(header),
name: TextCell::paint_str(self.colours.header, "Name"), name: TextCell::paint_str(self.colours.header, "Name"),
} }

View File

@ -14,7 +14,7 @@ use output::details::{Options as DetailsOptions, Row as DetailsRow, Render as De
use output::grid::Options as GridOptions; use output::grid::Options as GridOptions;
use output::file_name::{FileName, LinkStyle, Classify}; use output::file_name::{FileName, LinkStyle, Classify};
use output::table::{Table, Environment, Row as TableRow}; use output::table::{Table, Environment, Row as TableRow};
use output::tree::TreeParams; use output::tree::{TreeParams, TreeDepth};
pub struct Render<'a> { pub struct Render<'a> {
@ -120,7 +120,7 @@ impl<'a> Render<'a> {
let (ref mut table, ref mut rows) = tables[index]; let (ref mut table, ref mut rows) = tables[index];
table.add_widths(&row); 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); rows.push(details_row);
} }

View File

@ -88,12 +88,15 @@ pub struct TreeParams {
/// How many directories deep into the tree structure this is. Directories /// How many directories deep into the tree structure this is. Directories
/// on top have depth 0. /// on top have depth 0.
depth: usize, depth: TreeDepth,
/// Whether this is the last entry in the directory. /// Whether this is the last entry in the directory.
last: bool, last: bool,
} }
#[derive(Debug, Copy, Clone)]
pub struct TreeDepth(pub usize);
impl TreeTrunk { impl TreeTrunk {
/// Calculates the tree parts for an entry at the given depth and /// Calculates the tree parts for an entry at the given depth and
@ -108,40 +111,45 @@ impl TreeTrunk {
// If this isnt our first iteration, then update the tree parts thus // If this isnt our first iteration, then update the tree parts thus
// far to account for there being another row after it. // far to account for there being another row after it.
if let Some(last) = self.last_params { 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 // Make sure the stack has enough space, then add or modify another
// part into it. // part into it.
self.stack.resize(params.depth + 1, TreePart::Edge); self.stack.resize(params.depth.0 + 1, TreePart::Edge);
self.stack[params.depth] = if params.last { TreePart::Corner } else { TreePart::Edge }; self.stack[params.depth.0] = if params.last { TreePart::Corner } else { TreePart::Edge };
self.last_params = Some(params); self.last_params = Some(params);
// Return the tree parts as a slice of the stack. // Return the tree parts as a slice of the stack.
// //
// Ignoring the first component is specific to exa: when a user prints // Ignore the first element here to prevent a 'zeroth level' from
// a tree view for multiple directories, we dont want there to be a // appearing before the very first directory. This level would
// zeroth level connecting the initial directories. Otherwise, not // join unrelated directories without connecting to anything:
// only are unrelated directories seemingly connected to each other, //
// but the tree part of the first row doesnt connect 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..] &self.stack[1..]
} }
} }
impl TreeParams { impl TreeParams {
pub fn new(depth: usize, last: bool) -> TreeParams { pub fn new(depth: TreeDepth, last: bool) -> TreeParams {
TreeParams { depth, last } TreeParams { depth, last }
} }
pub fn is_zero(&self) -> bool { 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 { mod test {
use super::*; use super::*;
fn params(depth: usize, last: bool) -> TreeParams {
TreeParams::new(TreeDepth(depth), last)
}
#[test] #[test]
fn empty_at_first() { fn empty_at_first() {
let mut tt = TreeTrunk::default(); let mut tt = TreeTrunk::default();
assert_eq!(tt.new_row(TreeParams::new(0, true)), &[]); assert_eq!(tt.new_row(params(0, true)), &[]);
} }
#[test] #[test]
fn one_child() { fn one_child() {
let mut tt = TreeTrunk::default(); 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, true)), &[ TreePart::Corner ]); assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
} }
#[test] #[test]
fn two_children() { fn two_children() {
let mut tt = TreeTrunk::default(); 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(params(1, false)), &[ TreePart::Edge ]);
assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
} }
#[test] #[test]
fn two_times_two_children() { fn two_times_two_children() {
let mut tt = TreeTrunk::default(); let mut tt = TreeTrunk::default();
assert_eq!(tt.new_row(TreeParams::new(0, false)), &[]); assert_eq!(tt.new_row(params(0, false)), &[]);
assert_eq!(tt.new_row(TreeParams::new(1, false)), &[ TreePart::Edge ]); assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]);
assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
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(params(1, false)), &[ TreePart::Edge ]);
assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
} }
#[test] #[test]
fn two_times_two_nested_children() { fn two_times_two_nested_children() {
let mut tt = TreeTrunk::default(); 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(params(1, false)), &[ TreePart::Edge ]);
assert_eq!(tt.new_row(TreeParams::new(2, false)), &[ TreePart::Line, TreePart::Edge ]); assert_eq!(tt.new_row(params(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(2, true)), &[ TreePart::Line, TreePart::Corner ]);
assert_eq!(tt.new_row(TreeParams::new(1, true)), &[ TreePart::Corner ]); assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
assert_eq!(tt.new_row(TreeParams::new(2, false)), &[ TreePart::Blank, TreePart::Edge ]); assert_eq!(tt.new_row(params(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(2, true)), &[ TreePart::Blank, TreePart::Corner ]);
} }
} }