Przeglądaj źródła

Split source out into multiple files

Also, reverse the way columns are rendered: before, a column took a stat and a name to render; now, a file takes a column type to render. This means that most of the File data/methods can be private.
Ben S 11 lat temu
rodzic
commit
10b8f6f414
5 zmienionych plików z 142 dodań i 134 usunięć
  1. 0 2
      colours.rs
  2. 13 0
      column.rs
  3. 11 132
      exa.rs
  4. 102 0
      file.rs
  5. 16 0
      format.rs

+ 0 - 2
colours.rs

@@ -1,5 +1,3 @@
-#![allow(dead_code)]
-
 pub enum Colour {
     Black = 30, Red = 31, Green = 32, Yellow = 33, Blue = 34, Purple = 35, Cyan = 36, White = 37,
 }

+ 13 - 0
column.rs

@@ -0,0 +1,13 @@
+pub enum Column {
+    Permissions,
+    FileName,
+    FileSize(bool),
+}
+
+pub fn defaultColumns() -> ~[Column] {
+    return ~[
+        Permissions,
+        FileSize(false),
+        FileName,
+    ];
+}

+ 11 - 132
exa.rs

@@ -1,10 +1,15 @@
 extern crate getopts;
-use std::io::fs;
-use std::io;
 use std::os;
+use std::io;
+use std::io::fs;
+
+use file::File;
+use column::{Column, defaultColumns};
 
-use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
-mod colours;
+pub mod colours;
+pub mod column;
+pub mod format;
+pub mod file;
 
 struct Options {
     showInvisibles: bool,
@@ -36,97 +41,6 @@ fn main() {
     }
 }
 
-enum Permissions {
-    Permissions,
-}
-
-enum FileName {
-    FileName,
-}
-
-struct FileSize {
-    useSIPrefixes: bool,
-}
-
-trait Column {
-    fn display(&self, stat: &io::FileStat, filename: &str) -> ~str;
-}
-
-impl Column for FileName {
-    fn display(&self, stat: &io::FileStat, filename: &str) -> ~str {
-        file_colour(stat, filename).paint(filename.to_owned())
-    }
-}
-
-impl Column for Permissions {
-    fn display(&self, stat: &io::FileStat, filename: &str) -> ~str {
-        let bits = stat.perm;
-        return format!("{}{}{}{}{}{}{}{}{}{}",
-            type_char(stat.kind),
-            bit(bits, io::UserRead, ~"r", Yellow.bold()),
-            bit(bits, io::UserWrite, ~"w", Red.bold()),
-            bit(bits, io::UserExecute, ~"x", Green.bold().underline()),
-            bit(bits, io::GroupRead, ~"r", Yellow.normal()),
-            bit(bits, io::GroupWrite, ~"w", Red.normal()),
-            bit(bits, io::GroupExecute, ~"x", Green.normal()),
-            bit(bits, io::OtherRead, ~"r", Yellow.normal()),
-            bit(bits, io::OtherWrite, ~"w", Red.normal()),
-            bit(bits, io::OtherExecute, ~"x", Green.normal()),
-       );
-    }
-}
-
-impl Column for FileSize {
-    fn display(&self, stat: &io::FileStat, filename: &str) -> ~str {
-        let sizeStr = if self.useSIPrefixes {
-            formatBytes(stat.size, 1024, ~[ "B  ", "KiB", "MiB", "GiB", "TiB" ])
-        } else {
-            formatBytes(stat.size, 1000, ~[ "B ", "KB", "MB", "GB", "TB" ])
-        };
-
-        return if stat.kind == io::TypeDirectory {
-            Green.normal()
-        } else {
-            Green.bold()
-        }.paint(sizeStr);
-    }
-}
-
-fn formatBytes(mut amount: u64, kilo: u64, prefixes: ~[&str]) -> ~str {
-    let mut prefix = 0;
-    while amount > kilo {
-        amount /= kilo;
-        prefix += 1;
-    }
-    return format!("{:4}{}", amount, prefixes[prefix]);
-}
-
-// Each file is definitely going to get `stat`ted at least once, if
-// only to determine what kind of file it is, so carry the `stat`
-// result around with the file for safe keeping.
-struct File<'a> {
-    name: &'a str,
-    path: &'a Path,
-    stat: io::FileStat,
-}
-
-impl<'a> File<'a> {
-    fn from_path(path: &'a Path) -> File<'a> {
-        let filename: &str = path.filename_str().unwrap();
-
-        // We have to use lstat here instad of file.stat(), as it
-        // doesn't follow symbolic links. Otherwise, the stat() call
-        // will fail if it encounters a link that's target is
-        // non-existent.
-        let stat: io::FileStat = match fs::lstat(path) {
-            Ok(stat) => stat,
-            Err(e) => fail!("Couldn't stat {}: {}", filename, e),
-        };
-
-        return File { path: path, stat: stat, name: filename };
-    }
-}
-
 fn list(opts: Options, path: Path) {
     let mut files = match fs::readdir(&path) {
         Ok(files) => files,
@@ -140,13 +54,9 @@ fn list(opts: Options, path: Path) {
             continue;
         }
 
-        let columns = ~[
-            ~Permissions as ~Column,
-            ~FileSize { useSIPrefixes: false } as ~Column,
-            ~FileName as ~Column
-        ];
+        let columns = defaultColumns();
 
-        let mut cells = columns.iter().map(|c| c.display(&file.stat, file.name));
+        let mut cells = columns.iter().map(|c| file.display(c));
 
         let mut first = true;
         for cell in cells {
@@ -160,34 +70,3 @@ fn list(opts: Options, path: Path) {
         print!("\n");
     }
 }
-
-fn file_colour(stat: &io::FileStat, filename: &str) -> Style {
-    if stat.kind == io::TypeDirectory {
-        Blue.normal()
-    } else if stat.perm & io::UserExecute == io::UserExecute {
-        Green.normal()
-    } else if filename.ends_with("~") {
-        Black.bold()
-    } else {
-        Plain
-    }
-}
-
-fn bit(bits: u32, bit: u32, other: ~str, style: Style) -> ~str {
-    if bits & bit == bit {
-        style.paint(other)
-    } else {
-        Black.bold().paint(~"-")
-    }
-}
-
-fn type_char(t: io::FileType) -> ~str {
-    return match t {
-        io::TypeFile => ~".",
-        io::TypeDirectory => Blue.paint("d"),
-        io::TypeNamedPipe => Yellow.paint("|"),
-        io::TypeBlockSpecial => Purple.paint("s"),
-        io::TypeSymlink => Cyan.paint("l"),
-        _ => ~"?",
-    }
-}

+ 102 - 0
file.rs

@@ -0,0 +1,102 @@
+use std::io::fs;
+use std::io;
+
+use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
+use column::{Column, Permissions, FileName, FileSize};
+use format::{formatBinaryBytes, formatDecimalBytes};
+
+// Each file is definitely going to get `stat`ted at least once, if
+// only to determine what kind of file it is, so carry the `stat`
+// result around with the file for safe keeping.
+pub struct File<'a> {
+    name: &'a str,
+    path: &'a Path,
+    stat: io::FileStat,
+}
+
+impl<'a> File<'a> {
+    pub fn from_path(path: &'a Path) -> File<'a> {
+        let filename: &str = path.filename_str().unwrap();
+
+        // We have to use lstat here instad of file.stat(), as it
+        // doesn't follow symbolic links. Otherwise, the stat() call
+        // will fail if it encounters a link that's target is
+        // non-existent.
+        let stat: io::FileStat = match fs::lstat(path) {
+            Ok(stat) => stat,
+            Err(e) => fail!("Couldn't stat {}: {}", filename, e),
+        };
+
+        return File { path: path, stat: stat, name: filename };
+    }
+
+    pub fn display(&self, column: &Column) -> ~str {
+        match *column {
+            Permissions => self.permissions(),
+            FileName => self.file_colour().paint(self.name.to_owned()),
+            FileSize(si) => self.file_size(si),
+        }
+    }
+
+    fn file_size(&self, si: bool) -> ~str {
+        let sizeStr = if si {
+            formatBinaryBytes(self.stat.size)
+        } else {
+            formatDecimalBytes(self.stat.size)
+        };
+
+        return if self.stat.kind == io::TypeDirectory {
+            Green.normal()
+        } else {
+            Green.bold()
+        }.paint(sizeStr);
+    }
+
+    fn type_char(&self) -> ~str {
+        return match self.stat.kind {
+            io::TypeFile => ~".",
+            io::TypeDirectory => Blue.paint("d"),
+            io::TypeNamedPipe => Yellow.paint("|"),
+            io::TypeBlockSpecial => Purple.paint("s"),
+            io::TypeSymlink => Cyan.paint("l"),
+            _ => ~"?",
+        }
+    }
+
+
+    fn file_colour(&self) -> Style {
+        if self.stat.kind == io::TypeDirectory {
+            Blue.normal()
+        } else if self.stat.perm & io::UserExecute == io::UserExecute {
+            Green.normal()
+        } else if self.name.ends_with("~") {
+            Black.bold()
+        } else {
+            Plain
+        }
+    }
+
+    fn permissions(&self) -> ~str {
+        let bits = self.stat.perm;
+        return format!("{}{}{}{}{}{}{}{}{}{}",
+            self.type_char(),
+            bit(bits, io::UserRead, ~"r", Yellow.bold()),
+            bit(bits, io::UserWrite, ~"w", Red.bold()),
+            bit(bits, io::UserExecute, ~"x", Green.bold().underline()),
+            bit(bits, io::GroupRead, ~"r", Yellow.normal()),
+            bit(bits, io::GroupWrite, ~"w", Red.normal()),
+            bit(bits, io::GroupExecute, ~"x", Green.normal()),
+            bit(bits, io::OtherRead, ~"r", Yellow.normal()),
+            bit(bits, io::OtherWrite, ~"w", Red.normal()),
+            bit(bits, io::OtherExecute, ~"x", Green.normal()),
+       );
+    }
+}
+
+fn bit(bits: u32, bit: u32, other: ~str, style: Style) -> ~str {
+    if bits & bit == bit {
+        style.paint(other)
+    } else {
+        Black.bold().paint(~"-")
+    }
+}

+ 16 - 0
format.rs

@@ -0,0 +1,16 @@
+fn formatBytes(mut amount: u64, kilo: u64, prefixes: ~[&str]) -> ~str {
+    let mut prefix = 0;
+    while amount > kilo {
+        amount /= kilo;
+        prefix += 1;
+    }
+    return format!("{:4}{}", amount, prefixes[prefix]);
+}
+
+pub fn formatBinaryBytes(amount: u64) -> ~str {
+    formatBytes(amount, 1024, ~[ "B  ", "KiB", "MiB", "GiB", "TiB" ])
+}
+
+pub fn formatDecimalBytes(amount: u64) -> ~str {
+    formatBytes(amount, 1000, ~[ "B ", "KB", "MB", "GB", "TB" ])
+}