Explorar el Código

Refactor details view

Move most of the heavy lifting into a Table struct, which doesn't govern how the resulting table is *created*, only how it's *displayed*.
Ben S hace 11 años
padre
commit
134f9fc252
Se han modificado 3 ficheros con 121 adiciones y 67 borrados
  1. 1 1
      src/column.rs
  2. 2 2
      src/options.rs
  3. 118 64
      src/output/details.rs

+ 1 - 1
src/column.rs

@@ -4,7 +4,7 @@ use ansi_term::Style;
 
 use options::{SizeFormat, TimeType};
 
-#[derive(PartialEq, Debug, Copy)]
+#[derive(PartialEq, Debug, Copy, Clone)]
 pub enum Column {
     Permissions,
     FileSize(SizeFormat),

+ 2 - 2
src/options.rs

@@ -298,7 +298,7 @@ impl View {
     }
 }
 
-#[derive(PartialEq, Debug, Copy)]
+#[derive(PartialEq, Debug, Copy, Clone)]
 pub enum SizeFormat {
     DecimalBytes,
     BinaryBytes,
@@ -319,7 +319,7 @@ impl SizeFormat {
     }
 }
 
-#[derive(PartialEq, Debug, Copy)]
+#[derive(PartialEq, Debug, Copy, Clone)]
 pub enum TimeType {
     FileAccessed,
     FileModified,

+ 118 - 64
src/output/details.rs

@@ -18,7 +18,6 @@ pub struct Details {
 }
 
 impl Details {
-
     pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
         // The output gets formatted into columns, which looks nicer. To
         // do this, we have to write the results into a table, instead of
@@ -26,47 +25,120 @@ impl Details {
         // width of each column based on the length of the results and
         // padding the fields during output.
 
-        let columns = self.columns.for_dir(dir);
-        let locale = UserLocale::new();
-        let mut cache = OSUsers::empty_cache();
-        let mut table = Vec::new();
-        self.get_files(&columns[..], &mut cache, &locale, &mut table, files, 0);
-
-        if self.header {
-            let row = Row {
-                depth: 0,
-                cells: columns.iter().map(|c| Cell::paint(Plain.underline(), c.header())).collect(),
-                name: Plain.underline().paint("Name").to_string(),
-                last: false,
-                attrs: Vec::new(),
-                children: false,
-            };
-
-            table.insert(0, row);
+        // Almost all the heavy lifting is done in a Table object, which
+        // automatically calculates the width of each column and the
+        // appropriate padding.
+        let mut table = Table::with_columns(self.columns.for_dir(dir));
+        if self.header { table.add_header() }
+
+        self.add_files_to_table(&mut table, files, 0);
+        table.print_table(self.xattr, self.recurse.is_some());
+    }
+
+    /// Adds files to the table - recursively, if the `recurse` option
+    /// is present.
+    fn add_files_to_table(&self, table: &mut Table, src: &[File], depth: usize) {
+        for (index, file) in src.iter().enumerate() {
+            table.add_row(file, depth, index == src.len() - 1);
+
+            if let Some(r) = self.recurse {
+                if r.tree == false || r.is_too_deep(depth) {
+                    continue;
+                }
+
+                if let Some(ref dir) = file.this {
+                    let mut files = dir.files(true);
+                    self.filter.transform_files(&mut files);
+                    self.add_files_to_table(table, &files, depth + 1);
+                }
+            }
         }
+    }
+}
 
-        let column_widths: Vec<usize> = range(0, columns.len())
-            .map(|n| table.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
-            .collect();
+struct Row {
+    pub depth: usize,
+    pub cells: Vec<Cell>,
+    pub name: String,
+    pub last: bool,
+    pub attrs: Vec<Attribute>,
+    pub children: bool,
+}
+
+type ColumnInfo = (usize, Alignment);
+
+struct Table {
+    columns: Vec<Column>,
+    users: OSUsers,
+    locale: UserLocale,
+    rows: Vec<Row>,
+}
+
+impl Table {
+    fn with_columns(columns: Vec<Column>) -> Table {
+        Table {
+            columns: columns,
+            users: OSUsers::empty_cache(),
+            locale: UserLocale::new(),
+            rows: Vec::new(),
+        }
+    }
 
+    fn add_header(&mut self) {
+        let row = Row {
+            depth: 0,
+            cells: self.columns.iter().map(|c| Cell::paint(Plain.underline(), c.header())).collect(),
+            name: Plain.underline().paint("Name").to_string(),
+            last: false,
+            attrs: Vec::new(),
+            children: false,
+        };
+
+        self.rows.push(row);
+    }
+
+    fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
+        self.columns.clone().iter()
+                    .map(|c| file.display(c, &mut self.users, &self.locale))
+                    .collect()
+    }
+
+    fn add_row(&mut self, file: &File, depth: usize, last: bool) {
+        let row = Row {
+            depth: depth,
+            cells: self.cells_for_file(file),
+            name: file.file_name_view(),
+            last: last,
+            attrs: file.xattrs.clone(),
+            children: file.this.is_some(),
+        };
+
+        self.rows.push(row)
+    }
+
+    fn print_table(self, xattr: bool, show_children: bool) {
         let mut stack = Vec::new();
 
-        for row in table {
-            for (num, column) in columns.iter().enumerate() {
-                let padding = column_widths[num] - row.cells[num].length;
-                print!("{} ", column.alignment().pad_string(&row.cells[num].text, padding));
+        let column_widths: Vec<usize> = range(0, self.columns.len())
+            .map(|n| self.rows.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
+            .collect();
+
+        for row in self.rows.iter() {
+            for (n, width) in column_widths.iter().enumerate() {
+                let padding = width - row.cells[n].length;
+                print!("{} ", self.columns[n].alignment().pad_string(&row.cells[n].text, padding));
             }
 
-            if self.recurse.is_some() {
-                stack.resize(row.depth  + 1, "├──");
-                stack[row.depth] = if row.last { "└──" } else { "├──" };
+            if show_children {
+                stack.resize(row.depth + 1, TreePart::Edge);
+                stack[row.depth] = if row.last { TreePart::Corner } else { TreePart::Edge };
 
                 for i in 1 .. row.depth + 1 {
-                    print!("{}", GREY.paint(stack[i]));
+                    print!("{}", GREY.paint(stack[i].ascii_art()));
                 }
 
                 if row.children {
-                    stack[row.depth] = if row.last { "   " } else { "│  " };
+                    stack[row.depth] = if row.last { TreePart::Blank } else { TreePart::Line };
                 }
 
                 if row.depth != 0 {
@@ -76,7 +148,7 @@ impl Details {
 
             print!("{}\n", row.name);
 
-            if self.xattr {
+            if xattr {
                 let width = row.attrs.iter().map(|a| a.name().len()).max().unwrap_or(0);
                 for attr in row.attrs.iter() {
                     let name = attr.name();
@@ -88,45 +160,27 @@ impl Details {
             }
         }
     }
+}
 
-    fn get_files(&self, columns: &[Column], cache: &mut OSUsers, locale: &UserLocale, dest: &mut Vec<Row>, src: &[File], depth: usize) {
-        for (index, file) in src.iter().enumerate() {
-
-            let row = Row {
-                depth: depth,
-                cells: columns.iter().map(|c| file.display(c, cache, locale)).collect(),
-                name:  file.file_name_view(),
-                last:  index == src.len() - 1,
-                attrs: file.xattrs.clone(),
-                children: file.this.is_some(),
-            };
-
-            dest.push(row);
-
-            if let Some(r) = self.recurse {
-                if r.tree == false || r.is_too_deep(depth) {
-                    continue;
-                }
+#[derive(PartialEq, Debug, Clone)]
+enum TreePart {
+    Edge,
+    Corner,
+    Blank,
+    Line,
+}
 
-                if let Some(ref dir) = file.this {
-                    let mut files = dir.files(true);
-                    self.filter.transform_files(&mut files);
-                    self.get_files(columns, cache, locale, dest, &files, depth + 1);
-                }
-            }
+impl TreePart {
+    fn ascii_art(&self) -> &'static str {
+        match *self {
+            TreePart::Edge   => "├──",
+            TreePart::Line   => "│  ",
+            TreePart::Corner => "└──",
+            TreePart::Blank  => "   ",
         }
     }
 }
 
-struct Row {
-    pub depth: usize,
-    pub cells: Vec<Cell>,
-    pub name: String,
-    pub last: bool,
-    pub attrs: Vec<Attribute>,
-    pub children: bool,
-}
-
 pub struct UserLocale {
     pub time: locale::Time,
     pub numeric: locale::Numeric,