Initial commit

This commit is contained in:
Ben S 2014-05-03 11:30:37 +01:00
commit dc9668c8d6
4 changed files with 174 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
exa
*~

86
colours.rs Normal file
View File

@ -0,0 +1,86 @@
#![allow(dead_code)]
pub enum Colour {
Black = 30, Red = 31, Green = 32, Yellow = 33, Blue = 34, Purple = 35, Cyan = 36, White = 37,
}
pub enum Style {
Plain,
Foreground(Colour),
Style(StyleStruct),
}
struct StyleStruct {
foreground: Colour,
background: Option<Colour>,
bold: bool,
underline: bool,
}
impl Style {
pub fn paint(&self, input: ~str) -> ~str {
match *self {
Plain => input,
Foreground(c) => c.paint(input),
Style(s) => match s {
StyleStruct { foreground, background, bold, underline } => {
let bg: ~str = match background {
Some(c) => format!("{};", c as int + 10),
None => ~"",
};
let bo: ~str = if bold { ~"1;" } else { ~"" };
let un: ~str = if underline { ~"4;" } else { ~"" };
format!("\x1B[{}{}{}{}m{}\x1B[0m", bo, un, bg, foreground as int, input)
}
}
}
}
}
impl Style {
pub fn bold(&self) -> Style {
match *self {
Plain => Style(StyleStruct { foreground: White, background: None, bold: true, underline: false }),
Foreground(c) => Style(StyleStruct { foreground: c, background: None, bold: true, underline: false }),
Style(st) => Style(StyleStruct { foreground: st.foreground, background: st.background, bold: true, underline: false }),
}
}
pub fn underline(&self) -> Style {
match *self {
Plain => Style(StyleStruct { foreground: White, background: None, bold: false, underline: true }),
Foreground(c) => Style(StyleStruct { foreground: c, background: None, bold: false, underline: true }),
Style(st) => Style(StyleStruct { foreground: st.foreground, background: st.background, bold: false, underline: true }),
}
}
pub fn on(&self, background: Colour) -> Style {
match *self {
Plain => Style(StyleStruct { foreground: White, background: Some(background), bold: false, underline: false }),
Foreground(c) => Style(StyleStruct { foreground: c, background: Some(background), bold: false, underline: false }),
Style(st) => Style(StyleStruct { foreground: st.foreground, background: Some(background), bold: false, underline: false }),
}
}
}
impl Colour {
pub fn paint(&self, input: &str) -> ~str {
format!("\x1B[{}m{}\x1B[0m", *self as int, input)
}
pub fn underline(&self) -> Style {
Style(StyleStruct { foreground: *self, background: None, bold: false, underline: true })
}
pub fn bold(&self) -> Style {
Style(StyleStruct { foreground: *self, background: None, bold: true, underline: false })
}
pub fn normal(&self) -> Style {
Style(StyleStruct { foreground: *self, background: None, bold: false, underline: false })
}
pub fn on(&self, background: Colour) -> Style {
Style(StyleStruct { foreground: *self, background: Some(background), bold: false, underline: false })
}
}

86
exa.rs Normal file
View File

@ -0,0 +1,86 @@
use std::io::fs;
use std::io;
use std::os;
use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
mod colours;
fn main() {
match os::args().as_slice() {
[] => unreachable!(),
[_] => { list(Path::new(".")) },
[_, ref p] => { list(Path::new(p.as_slice())) },
_ => { fail!("args?") },
}
}
fn list(path: Path) {
let mut files = match fs::readdir(&path) {
Ok(files) => files,
Err(e) => fail!("readdir: {}", e),
};
files.sort_by(|a, b| a.filename_str().cmp(&b.filename_str()));
for file in files.iter() {
let filename: &str = file.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(file) {
Ok(stat) => stat,
Err(e) => fail!("Couldn't stat {}: {}", filename, e),
};
let colour = file_colour(&stat, filename);
println!("{} {}", perm_str(&stat), colour.paint(filename.to_owned()));
}
}
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 perm_str(stat: &io::FileStat) -> ~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()),
);
}
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"),
_ => ~"?",
}
}

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB