exa.rs 5.9 KB
Newer Older
B
Ben S 已提交
1
#![warn(trivial_casts, trivial_numeric_casts)]
2
#![warn(unused_results)]
B
Ben S 已提交
3

4
extern crate ansi_term;
5
extern crate datetime;
6
extern crate getopts;
B
Ben S 已提交
7
extern crate glob;
8
extern crate libc;
9
extern crate locale;
10
extern crate natord;
B
Benjamin Sago 已提交
11
extern crate num_cpus;
12
extern crate number_prefix;
B
Ben S 已提交
13
extern crate scoped_threadpool;
B
Ben S 已提交
14
extern crate term_grid;
B
Ben S 已提交
15
extern crate unicode_width;
B
Ben S 已提交
16
extern crate users;
B
Ben S 已提交
17
extern crate zoneinfo_compiled;
B
Ben S 已提交
18

B
Ben S 已提交
19 20
#[cfg(feature="git")] extern crate git2;
#[macro_use] extern crate lazy_static;
B
Ben S 已提交
21

B
Benjamin Sago 已提交
22
use std::ffi::OsStr;
23
use std::io::{stderr, Write, Result as IOResult};
B
Ben S 已提交
24
use std::path::{Component, Path};
B
Ben S 已提交
25

26 27
use ansi_term::{ANSIStrings, Style};

B
Benjamin Sago 已提交
28
use fs::{Dir, File};
29
use options::{Options, View};
B
Benjamin Sago 已提交
30
pub use options::Misfire;
31
use output::escape;
B
Ben S 已提交
32

B
Benjamin Sago 已提交
33
mod fs;
34
mod info;
35 36 37
mod options;
mod output;
mod term;
B
Ben S 已提交
38

B
Ben S 已提交
39

40
/// The main program wrapper.
B
Benjamin Sago 已提交
41
pub struct Exa<'w, W: Write + 'w> {
42 43

    /// List of command-line options, having been successfully parsed.
B
Benjamin Sago 已提交
44
    pub options: Options,
45 46 47 48

    /// The output handle that we write to. When running the program normally,
    /// this will be `std::io::Stdout`, but it can accept any struct that’s
    /// `Write` so we can write into, say, a vector for testing.
B
Benjamin Sago 已提交
49 50 51 52 53
    pub writer: &'w mut W,

    /// List of the free command-line arguments that should correspond to file
    /// names (anything that isn’t an option).
    pub args: Vec<String>,
54
}
B
Benjamin Sago 已提交
55

56
impl<'w, W: Write + 'w> Exa<'w, W> {
B
Benjamin Sago 已提交
57 58 59 60 61 62 63 64 65
    pub fn new<S>(args: &[S], writer: &'w mut W) -> Result<Exa<'w, W>, Misfire>
    where S: AsRef<OsStr> {
        Options::getopts(args).map(move |(opts, args)| Exa {
            options: opts,
            writer:  writer,
            args:    args,
        })
    }

66
    pub fn run(&mut self) -> IOResult<i32> {
B
Ben S 已提交
67 68
        let mut files = Vec::new();
        let mut dirs = Vec::new();
69
        let mut exit_status = 0;
B
bp 已提交
70

B
Benjamin Sago 已提交
71 72 73
        // List the current directory by default, like ls.
        if self.args.is_empty() {
            self.args.push(".".to_owned());
B
Ben S 已提交
74 75
        }

76
        for file_name in &self.args {
B
Ben S 已提交
77 78
            match File::from_path(Path::new(&file_name), None) {
                Err(e) => {
79
                    exit_status = 2;
80
                    writeln!(stderr(), "{}: {}", file_name, e)?;
B
Ben S 已提交
81 82 83 84 85
                },
                Ok(f) => {
                    if f.is_directory() && !self.options.dir_action.treat_dirs_as_files() {
                        match f.to_dir(self.options.should_scan_for_git()) {
                            Ok(d) => dirs.push(d),
86
                            Err(e) => writeln!(stderr(), "{}: {}", file_name, e)?,
87 88
                        }
                    }
B
Ben S 已提交
89 90
                    else {
                        files.push(f);
91
                    }
B
Ben S 已提交
92
                },
B
Ben S 已提交
93 94
            }
        }
B
Ben S 已提交
95

B
Benjamin Sago 已提交
96 97 98 99
        // We want to print a directory’s name before we list it, *except* in
        // the case where it’s the only directory, *except* if there are any
        // files to print as well. (It’s a double negative)

B
Benjamin Sago 已提交
100
        let no_files = files.is_empty();
101 102
        let is_only_dir = dirs.len() == 1 && no_files;

B
Ben S 已提交
103
        self.options.filter.filter_argument_files(&mut files);
104
        self.print_files(None, files)?;
B
Ben S 已提交
105

106
        self.print_dirs(dirs, no_files, is_only_dir, exit_status)
107
    }
108

109
    fn print_dirs(&mut self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool, exit_status: i32) -> IOResult<i32> {
B
Ben S 已提交
110
        for dir in dir_files {
111

B
Benjamin Sago 已提交
112 113
            // Put a gap between directories, or between the list of files and
            // the first directory.
114 115 116 117
            if first {
                first = false;
            }
            else {
118
                write!(self.writer, "\n")?;
119 120
            }

B
Ben S 已提交
121
            if !is_only_dir {
122 123 124
                let mut bits = Vec::new();
                escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default());
                writeln!(self.writer, "{}:", ANSIStrings(&bits))?;
B
Ben S 已提交
125
            }
B
Ben S 已提交
126

B
Ben S 已提交
127 128 129 130
            let mut children = Vec::new();
            for file in dir.files() {
                match file {
                    Ok(file)       => children.push(file),
131
                    Err((path, e)) => writeln!(stderr(), "[{}: {}]", path.display(), e)?,
B
Ben S 已提交
132 133 134
                }
            };

B
Ben S 已提交
135
            self.options.filter.filter_child_files(&mut children);
136
            self.options.filter.sort_files(&mut children);
B
Ben S 已提交
137 138 139 140

            if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
                let depth = dir.path.components().filter(|&c| c != Component::CurDir).count() + 1;
                if !recurse_opts.tree && !recurse_opts.is_too_deep(depth) {
B
Ben S 已提交
141

B
Ben S 已提交
142 143 144 145
                    let mut child_dirs = Vec::new();
                    for child_dir in children.iter().filter(|f| f.is_directory()) {
                        match child_dir.to_dir(false) {
                            Ok(d)  => child_dirs.push(d),
146
                            Err(e) => writeln!(stderr(), "{}: {}", child_dir.path.display(), e)?,
147
                        }
B
Ben S 已提交
148 149
                    }

150
                    self.print_files(Some(&dir), children)?;
151 152 153 154
                    match self.print_dirs(child_dirs, false, false, exit_status) {
                        Ok(_) => (),
                        Err(e) => return Err(e),
                    }
B
Ben S 已提交
155
                    continue;
156
                }
B
Ben S 已提交
157 158
            }

159
            self.print_files(Some(&dir), children)?;
160
        }
161

162
        Ok(exit_status)
163
    }
164

B
Benjamin Sago 已提交
165 166 167
    /// Prints the list of files using whichever view is selected.
    /// For various annoying logistical reasons, each one handles
    /// printing differently...
168
    fn print_files(&mut self, dir: Option<&Dir>, files: Vec<File>) -> IOResult<()> {
169 170
        if !files.is_empty() {
            match self.options.view {
B
Ben S 已提交
171 172 173 174
                View::Grid(ref g)         => g.view(&files, self.writer),
                View::Details(ref d)      => d.view(dir, files, self.writer),
                View::GridDetails(ref gd) => gd.view(dir, files, self.writer),
                View::Lines(ref l)        => l.view(files, self.writer),
175 176 177 178
            }
        }
        else {
            Ok(())
179
        }
180
    }
181
}