diff --git a/src/dir.rs b/src/dir.rs index e6cd649..c1d5afc 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -78,9 +78,9 @@ pub struct Files<'dir> { } impl<'dir> Iterator for Files<'dir> { - type Item = io::Result>; + type Item = Result, (PathBuf, io::Error)>; fn next(&mut self) -> Option { - self.inner.next().map(|path| File::from_path(path, Some(self.dir), self.recurse)) + self.inner.next().map(|path| File::from_path(path, Some(self.dir), self.recurse).map_err(|t| (path.clone(), t))) } } \ No newline at end of file diff --git a/src/file.rs b/src/file.rs index 391fdc3..e0bf987 100644 --- a/src/file.rs +++ b/src/file.rs @@ -27,23 +27,23 @@ use self::fields as f; pub struct File<'dir> { /// This file's name, as a UTF-8 encoded String. - pub name: String, + pub name: String, /// The file's name's extension, if present, extracted from the name. This /// is queried a lot, so it's worth being cached. - pub ext: Option, + pub ext: Option, /// The path that begat this file. Even though the file's name is /// extracted, the path needs to be kept around, as certain operations /// involve looking up the file's absolute location (such as the Git /// status, or searching for compiled files). - pub path: PathBuf, + pub path: PathBuf, /// A cached `metadata` call for this file. This is queried multiple /// times, and is *not* cached by the OS, as it could easily change /// between invocations - but exa is so short-lived it's better to just /// cache it. - pub metadata: fs::Metadata, + pub metadata: fs::Metadata, /// List of this file's extended attributes. These are only loaded if the /// `xattr` feature is in use. @@ -57,11 +57,11 @@ pub struct File<'dir> { /// However, *directories* that get passed in will produce files that /// contain a reference to it, which is used in certain operations (such /// as looking up a file's Git status). - pub dir: Option<&'dir Dir>, + pub dir: Option<&'dir Dir>, /// If this `File` is also a directory, then this field is the same file /// as a `Dir`. - pub this: Option, + pub this: Option>, } impl<'dir> File<'dir> { @@ -82,7 +82,7 @@ impl<'dir> File<'dir> { // that represents the current File as a directory, if it is a // directory. This is used for the --tree option. let this = if recurse && metadata.is_dir() { - Dir::readdir(path, false).ok() + Some(Dir::readdir(path, false)) } else { None diff --git a/src/main.rs b/src/main.rs index a5f180c..33fca6e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![feature(iter_arith)] #![feature(convert, fs_mode)] #![feature(slice_splits, vec_resize)] @@ -146,7 +147,15 @@ impl<'dir> Exa<'dir> { match Dir::readdir(&dir_path, self.options.should_scan_for_git()) { Ok(ref dir) => { - let mut files = dir.files(false).flat_map(|f| f).collect(); + let mut files = Vec::new(); + + for file in dir.files(true) { + match file { + Ok(file) => files.push(file), + Err((path, e)) => println!("[{}: {}]", path.display(), e), + } + } + self.options.transform_files(&mut files); // When recursing, add any directories to the dirs stack diff --git a/src/output/details.rs b/src/output/details.rs index cb2f2ea..6be3b68 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -1,4 +1,7 @@ +use std::error::Error; +use std::io; use std::iter::repeat; +use std::path::Path; use std::string::ToString; use colours::Colours; @@ -98,11 +101,24 @@ impl Details { // Use the filter to remove unwanted files *before* expanding // them, so we don't examine any directories that wouldn't // have their contents listed anyway. - if let Some(ref dir) = file.this { - let mut files = dir.files(true).flat_map(|f| f).collect(); + match file.this { + Some(Ok(ref dir)) => { + let mut files = Vec::new(); - filter.transform_files(&mut files); - self.add_files_to_table(table, &files, depth + 1); + for file_to_add in dir.files(true) { + match file_to_add { + Ok(f) => files.push(f), + Err((path, e)) => table.add_error(&e, depth + 1, false, Some(&*path)), + } + } + + filter.transform_files(&mut files); + self.add_files_to_table(table, &files, depth + 1); + }, + Some(Err(ref e)) => { + table.add_error(e, depth + 1, true, None); + }, + None => {}, } } } @@ -221,6 +237,24 @@ impl Table where U: Users { self.rows.push(row); } + pub fn add_error(&mut self, error: &io::Error, depth: usize, last: bool, path: Option<&Path>) { + let error_message = match path { + Some(path) => format!("<{}: {}>", path.display(), error), + None => format!("<{}>", error), + }; + + let row = Row { + depth: depth, + cells: None, + name: Cell::paint(self.colours.broken_arrow, &error_message), + last: last, + attrs: Vec::new(), + children: false, + }; + + self.rows.push(row); + } + /// Get the cells for the given file, and add the result to the table. pub fn add_file(&mut self, file: &File, depth: usize, last: bool, links: bool) { let cells = self.cells_for_file(file); @@ -420,6 +454,8 @@ impl Table where U: Users { .map(|n| self.rows.iter().map(|row| row.column_width(n)).max().unwrap_or(0)) .collect(); + let total_width: usize = self.columns.len() + column_widths.iter().sum::(); + for row in self.rows.iter() { let mut cell = Cell::empty(); @@ -434,7 +470,7 @@ impl Table where U: Users { } } else { - cell.add_spaces(column_widths.len()) + cell.add_spaces(total_width) } let mut filename = String::new();