From cbd2f1fa37c2e0f33ecd27fb3c01edcb1182d37f Mon Sep 17 00:00:00 2001 From: Ben S Date: Mon, 24 Nov 2014 17:03:36 +0000 Subject: [PATCH] List files and directories separately This finally fixes the issue where trying to list a file causes a crash. Also, tidy up some of the uses of references. --- src/dir.rs | 2 +- src/exa.rs | 46 +++++++++++++++++++++++++++++++--------------- src/file.rs | 19 +++++++++++-------- src/filetype.rs | 2 +- src/options.rs | 23 +++++++++++------------ 5 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/dir.rs b/src/dir.rs index 5b531b8..4c58be5 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -24,7 +24,7 @@ impl<'a> Dir<'a> { let mut files = vec![]; for path in self.contents.iter() { - match File::from_path(path, self) { + match File::from_path(path.clone(), Some(self)) { Ok(file) => files.push(file), Err(e) => println!("{}: {}", path.display(), e), } diff --git a/src/exa.rs b/src/exa.rs index f81432b..df82445 100644 --- a/src/exa.rs +++ b/src/exa.rs @@ -5,6 +5,8 @@ extern crate ansi_term; extern crate unicode; use std::os; +use std::io::fs; +use std::io::FileType::TypeDirectory; use file::File; use dir::Dir; @@ -34,14 +36,24 @@ fn main() { }; } -fn exa(opts: &Options) { - let mut first = true; - +fn exa(opts: &Options) { // It's only worth printing out directory names if the user supplied // more than one of them. - let print_dir_names = opts.dirs.len() > 1; + let print_dir_names = opts.path_strs.len() > 1; + let (dir_strs, file_strs) = opts.path_strs.clone().partition(|n| fs::stat(&Path::new(n)).unwrap().kind == TypeDirectory); + + let mut first = file_strs.is_empty(); + let mut files = vec![]; + for f in file_strs.iter() { + match File::from_path(Path::new(f), None) { + Ok(file) => files.push(file), + Err(e) => println!("{}: {}", f, e), + } + } + + view(opts, files); - for dir_name in opts.dirs.clone().into_iter() { + for dir_name in dir_strs.into_iter() { if first { first = false; } @@ -52,17 +64,13 @@ fn exa(opts: &Options) { match Dir::readdir(Path::new(dir_name.clone())) { Ok(dir) => { let unsorted_files = dir.files(); - let files: Vec<&File> = opts.transform_files(&unsorted_files); + let files: Vec = opts.transform_files(unsorted_files); if print_dir_names { println!("{}:", dir_name); } - match opts.view { - View::Details(ref cols) => details_view(opts, cols, files), - View::Lines => lines_view(files), - View::Grid(across, width) => grid_view(across, width, files), - } + view(opts, files); } Err(e) => { println!("{}: {}", dir_name, e); @@ -72,13 +80,21 @@ fn exa(opts: &Options) { } } -fn lines_view(files: Vec<&File>) { +fn view(options: &Options, files: Vec) { + match options.view { + View::Details(ref cols) => details_view(options, cols, files), + View::Lines => lines_view(files), + View::Grid(across, width) => grid_view(across, width, files), + } +} + +fn lines_view(files: Vec) { for file in files.iter() { println!("{}", file.file_name()); } } -fn grid_view(across: bool, console_width: uint, files: Vec<&File>) { +fn grid_view(across: bool, console_width: uint, files: Vec) { let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0); let num_columns = (console_width + 1) / (max_column_length + 1); let count = files.len(); @@ -101,7 +117,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<&File>) { continue; } - let file = files[num]; + let ref file = files[num]; let file_name = file.name.clone(); let styled_name = file.file_colour().paint(file_name.as_slice()); if x == num_columns - 1 { @@ -115,7 +131,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<&File>) { } } -fn details_view(options: &Options, columns: &Vec, files: Vec<&File>) { +fn details_view(options: &Options, columns: &Vec, files: Vec) { // 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 diff --git a/src/file.rs b/src/file.rs index dc45202..4343e18 100644 --- a/src/file.rs +++ b/src/file.rs @@ -23,15 +23,15 @@ pub static GREY: Colour = Fixed(244); pub struct File<'a> { pub name: String, - pub dir: &'a Dir<'a>, + pub dir: Option<&'a Dir<'a>>, pub ext: Option, - pub path: &'a Path, + pub path: Path, pub stat: io::FileStat, pub parts: Vec, } impl<'a> File<'a> { - pub fn from_path(path: &'a Path, parent: &'a Dir) -> IoResult> { + pub fn from_path(path: Path, parent: Option<&'a Dir<'a>>) -> IoResult> { let v = path.filename().unwrap(); // fails if / or . or .. let filename = String::from_utf8(v.to_vec()).unwrap_or_else(|_| panic!("Name was not valid UTF-8")); @@ -39,8 +39,8 @@ impl<'a> File<'a> { // symbolic links. Otherwise, the stat() call will fail if it // encounters a link that's target is non-existent. - fs::lstat(path).map(|stat| File { - path: path, + fs::lstat(&path).map(|stat| File { + path: path.clone(), dir: parent, stat: stat, name: filename.clone(), @@ -158,9 +158,12 @@ impl<'a> File<'a> { let name = self.name.as_slice(); let displayed_name = self.file_colour().paint(name); if self.stat.kind == io::TypeSymlink { - match fs::readlink(self.path) { + match fs::readlink(&self.path) { Ok(path) => { - let target_path = if path.is_absolute() { path } else { self.dir.path.join(path) }; + let target_path = match self.dir { + Some(dir) => dir.path.join(path), + None => path, + }; format!("{} {}", displayed_name, self.target_file_name_and_arrow(target_path)) } Err(_) => displayed_name, @@ -180,7 +183,7 @@ impl<'a> File<'a> { let filename = String::from_utf8_lossy(v).to_string(); let link_target = fs::stat(&target_path).map(|stat| File { - path: &target_path, + path: target_path.clone(), dir: self.dir, stat: stat, name: filename.clone(), diff --git a/src/filetype.rs b/src/filetype.rs index 958deb8..6b4beca 100644 --- a/src/filetype.rs +++ b/src/filetype.rs @@ -126,7 +126,7 @@ impl<'a> HasType for File<'a> { if source_files.len() == 0 { return Normal; } - else if source_files.iter().any(|path| self.dir.contains(path)) { + else if source_files.iter().any(|path| self.dir.map(|d| d.contains(path)).unwrap_or(false)) { return Temp; } else { diff --git a/src/options.rs b/src/options.rs index b6651c4..39882e6 100644 --- a/src/options.rs +++ b/src/options.rs @@ -31,12 +31,12 @@ pub enum View { } pub struct Options { + pub header: bool, + pub path_strs: Vec, + pub reverse: bool, pub show_invisibles: bool, pub sort_field: SortField, - pub reverse: bool, - pub dirs: Vec, pub view: View, - pub header: bool, } @@ -60,11 +60,11 @@ impl Options { match getopts::getopts(args.tail(), opts) { Err(f) => Err(f), Ok(ref matches) => Ok(Options { - show_invisibles: matches.opt_present("all"), - reverse: matches.opt_present("reverse"), header: matches.opt_present("header"), + path_strs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() }, + reverse: matches.opt_present("reverse"), + show_invisibles: matches.opt_present("all"), sort_field: matches.opt_str("sort").map(|word| SortField::from_word(word)).unwrap_or(SortField::Name), - dirs: if matches.free.is_empty() { vec![ ".".to_string() ] } else { matches.free.clone() }, view: Options::view(matches), }) } @@ -111,8 +111,7 @@ impl Options { } columns.push(FileName); - - return columns; + columns } fn should_display(&self, f: &File) -> bool { @@ -124,9 +123,9 @@ impl Options { } } - pub fn transform_files<'a>(&self, unordered_files: &'a Vec>) -> Vec<&'a File<'a>> { - let mut files: Vec<&'a File<'a>> = unordered_files.iter() - .filter(|&f| self.should_display(f)) + pub fn transform_files<'a>(&self, unordered_files: Vec>) -> Vec> { + let mut files: Vec> = unordered_files.into_iter() + .filter(|f| self.should_display(f)) .collect(); match self.sort_field { @@ -145,6 +144,6 @@ impl Options { files.reverse(); } - return files; + files } } -- GitLab