summaryrefslogtreecommitdiffstats
path: root/src/output/details.rs
blob: 8009a2afca75537da2670fdbad0cf35920025eb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use column::{Column, Cell};
use dir::Dir;
use file::{File, GREY};
use options::{Columns, FileFilter};
use users::OSUsers;

use locale;
use ansi_term::Style::Plain;

#[derive(PartialEq, Debug, Copy)]
pub struct Details {
    pub columns: Columns,
    pub header: bool,
    pub tree: bool,
    pub filter: FileFilter,
}

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
        // displaying each file immediately, then calculating the maximum
        // 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,
                children: false,
            };

            table.insert(0, row);
        }

        let column_widths: Vec<usize> = range(0, columns.len())
            .map(|n| table.iter().map(|row| row.cells[n].length).max().unwrap_or(0))
            .collect();

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

            if self.tree {
                stack.resize(row.depth  + 1, "├──");
                stack[row.depth] = if row.last { "└──" } else { "├──" };

                for i in 1 .. row.depth + 1 {
                    print!("{}", GREY.paint(stack[i]));
                }

                if row.children {
                    stack[row.depth] = if row.last { "   " } else { "│  " };
                }

                if row.depth != 0 {
                    print!(" ");
                }
            }

            print!("{}\n", row.name);
        }
    }

    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,
                children: file.this.is_some(),
            };

            dest.push(row);

            if self.tree {
                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.as_slice(), depth + 1);
                }
            }
        }
    }
}

struct Row {
    pub depth: usize,
    pub cells: Vec<Cell>,
    pub name: String,
    pub last: bool,
    pub children: bool,
}

pub struct UserLocale {
    pub time: locale::Time,
    pub numeric: locale::Numeric,
}

impl UserLocale {
    pub fn new() -> UserLocale {
        UserLocale {
            time: locale::Time::load_user_locale().unwrap_or_else(|_| locale::Time::english()),
            numeric: locale::Numeric::load_user_locale().unwrap_or_else(|_| locale::Numeric::english()),
        }
    }

    pub fn default() -> UserLocale {
        UserLocale {
            time: locale::Time::english(),
            numeric: locale::Numeric::english(),
        }
    }
}