Browse Source

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.
Ben S 11 years ago
parent
commit
cbd2f1fa37
5 changed files with 55 additions and 37 deletions
  1. 1 1
      src/dir.rs
  2. 31 15
      src/exa.rs
  3. 11 8
      src/file.rs
  4. 1 1
      src/filetype.rs
  5. 11 12
      src/options.rs

+ 1 - 1
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),
             }

+ 31 - 15
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<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

+ 11 - 8
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<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(),

+ 1 - 1
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 {

+ 11 - 12
src/options.rs

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