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.
This commit is contained in:
Ben S 2014-11-24 17:03:36 +00:00
parent c7417e0bca
commit cbd2f1fa37
5 changed files with 55 additions and 37 deletions

View File

@ -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),
}

View File

@ -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);
for dir_name in opts.dirs.clone().into_iter() {
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 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<File> = 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<File>) {
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<File>) {
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<File>) {
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<Column>, files: Vec<&File>) {
fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<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

View File

@ -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<String>,
pub path: &'a Path,
pub path: Path,
pub stat: io::FileStat,
pub parts: Vec<SortPart>,
}
impl<'a> File<'a> {
pub fn from_path(path: &'a Path, parent: &'a Dir) -> IoResult<File<'a>> {
pub fn from_path(path: Path, parent: Option<&'a Dir<'a>>) -> IoResult<File<'a>> {
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(),

View File

@ -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 {

View File

@ -31,12 +31,12 @@ pub enum View {
}
pub struct Options {
pub header: bool,
pub path_strs: Vec<String>,
pub reverse: bool,
pub show_invisibles: bool,
pub sort_field: SortField,
pub reverse: bool,
pub dirs: Vec<String>,
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<File<'a>>) -> 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<File<'a>>) -> Vec<File<'a>> {
let mut files: Vec<File<'a>> = 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
}
}