file.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. use std::io::fs;
  2. use std::io;
  3. use colours::{Plain, Style, Black, Red, Green, Yellow, Blue, Purple, Cyan};
  4. use column::{Column, Permissions, FileName, FileSize};
  5. use format::{formatBinaryBytes, formatDecimalBytes};
  6. // Each file is definitely going to get `stat`ted at least once, if
  7. // only to determine what kind of file it is, so carry the `stat`
  8. // result around with the file for safe keeping.
  9. pub struct File<'a> {
  10. name: &'a str,
  11. path: &'a Path,
  12. stat: io::FileStat,
  13. }
  14. impl<'a> File<'a> {
  15. pub fn from_path(path: &'a Path) -> File<'a> {
  16. let filename: &str = path.filename_str().unwrap();
  17. // We have to use lstat here instad of file.stat(), as it
  18. // doesn't follow symbolic links. Otherwise, the stat() call
  19. // will fail if it encounters a link that's target is
  20. // non-existent.
  21. let stat: io::FileStat = match fs::lstat(path) {
  22. Ok(stat) => stat,
  23. Err(e) => fail!("Couldn't stat {}: {}", filename, e),
  24. };
  25. return File { path: path, stat: stat, name: filename };
  26. }
  27. pub fn display(&self, column: &Column) -> ~str {
  28. match *column {
  29. Permissions => self.permissions(),
  30. FileName => self.file_colour().paint(self.name.to_owned()),
  31. FileSize(si) => self.file_size(si),
  32. }
  33. }
  34. fn file_size(&self, si: bool) -> ~str {
  35. let sizeStr = if si {
  36. formatBinaryBytes(self.stat.size)
  37. } else {
  38. formatDecimalBytes(self.stat.size)
  39. };
  40. return if self.stat.kind == io::TypeDirectory {
  41. Green.normal()
  42. } else {
  43. Green.bold()
  44. }.paint(sizeStr);
  45. }
  46. fn type_char(&self) -> ~str {
  47. return match self.stat.kind {
  48. io::TypeFile => ~".",
  49. io::TypeDirectory => Blue.paint("d"),
  50. io::TypeNamedPipe => Yellow.paint("|"),
  51. io::TypeBlockSpecial => Purple.paint("s"),
  52. io::TypeSymlink => Cyan.paint("l"),
  53. _ => ~"?",
  54. }
  55. }
  56. fn file_colour(&self) -> Style {
  57. if self.stat.kind == io::TypeDirectory {
  58. Blue.normal()
  59. } else if self.stat.perm & io::UserExecute == io::UserExecute {
  60. Green.normal()
  61. } else if self.name.ends_with("~") {
  62. Black.bold()
  63. } else {
  64. Plain
  65. }
  66. }
  67. fn permissions(&self) -> ~str {
  68. let bits = self.stat.perm;
  69. return format!("{}{}{}{}{}{}{}{}{}{}",
  70. self.type_char(),
  71. bit(bits, io::UserRead, ~"r", Yellow.bold()),
  72. bit(bits, io::UserWrite, ~"w", Red.bold()),
  73. bit(bits, io::UserExecute, ~"x", Green.bold().underline()),
  74. bit(bits, io::GroupRead, ~"r", Yellow.normal()),
  75. bit(bits, io::GroupWrite, ~"w", Red.normal()),
  76. bit(bits, io::GroupExecute, ~"x", Green.normal()),
  77. bit(bits, io::OtherRead, ~"r", Yellow.normal()),
  78. bit(bits, io::OtherWrite, ~"w", Red.normal()),
  79. bit(bits, io::OtherExecute, ~"x", Green.normal()),
  80. );
  81. }
  82. }
  83. fn bit(bits: u32, bit: u32, other: ~str, style: Style) -> ~str {
  84. if bits & bit == bit {
  85. style.paint(other)
  86. } else {
  87. Black.bold().paint(~"-")
  88. }
  89. }