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::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<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 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"),
}

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::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);
}

View File

@ -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 isnt 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 dont 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 doesnt 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
// ├── 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 ]);
}
}